Oh! What I was doing in my examples was pretending there was a version of
Elm that natively supported `command` and `update` methods instead of just
`update`. I was exploring what that could look like, how it would affect
things.
I don't have a complete example in mind. The original post was more to get
the idea out there and discuss it.
So, I suppose I could also explore putting the command/event separation
into practice as subsets of Msg which should work with non-imaginary Elm.
type Act
= UpdateCustomer customer
| ... -- another command
type Evt
= NameChanged name
| CustomerUpdateRequested
| CustomerUpdateFailed err
| CustomerUpdateSucceeded
type Msg
= Action act
| Event evt
doSideEffects: Act -> Model -> Cmd Evt
doSideEffects act model =
case act of
SubmitCustomerChanges customer ->
Cmd.batch
[ Cmd.fromEvt CustomerUpdateRequested
, callServerWithCustomer customer -- or model.Customer?
]
...
updateModel: Evt -> Model -> Model
... -- implementation as previous
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Action act ->
(model, doSideEffects act model)
Event evt ->
(updateModel evt model, Cmd.none)
Apologize for errors... writing code in this groups editor.
Anyway, this is the other choice I mentioned where you could let the view
send either a command or event. This could prevent redundant "commands",
example: `onInput`s which only change the model could issue an event
directly instead of a command first. I thought this would require more
discipline to use, but now that I see it, I think using "commands" (Action
above) and "events" appropriately is enforced by `update`. If you want an
event when an action happens, you could use a Cmd.batch (as in
CustomerUpdateRequested above). That enforces model updates being separate
messages from effects.
For event-sourcing, you could replay only the `Event`s in complete
isolation of the outside world to arrive at an interesting UI state. For
example, an incorrect state that was bug reported.
On Thursday, August 11, 2016 at 2:21:42 PM UTC-5, Kasey Speakman wrote:
>
> I'm not sure I like it either. Seems like a lot of UI commands would just
> be auto-converted to events by `command`.
>
> However, in your example, I'm not sure what your filter code is about (my
> ignorance). If I am understanding it right, it's filling in for the case
> when you need to respond to an event and issue another command. This also
> seems like a separate concern from what I mentioned.
>
> On Thursday, August 11, 2016 at 1:53:15 PM UTC-5, OvermindDL1 wrote:
>>
>> Yeah I was playing with the idea for more message types though still not
>> convinced yet. Let's go hog-wild for a second to see how it would look
>> with a fairly major overhaul and multiple message types though:
>> ```elm
>>
>> {-| Msg is used for global message dispatching, handled only by the
>> `filters` callback -}type Msg
>> = ConnectTo Int
>> | RoomConnected Int
>> | ReceivedMessage String
>> | MsgOnInput String
>> | SendMsgDo
>> type CmdMsg
>> = FocusInputBox
>> | ScrollInputBox
>> | ConnectToRoom Int
>> | SendMsgToRoom String
>> type UpdMsg
>> = JoinedRoom
>> | MsgTyped String
>> | MsgSent
>> type alias Model =
>> { uid : Int
>> , msg : String
>> , msgs : List String
>> }
>> init : ( Model, Cmd Msg )init = ( Model 0 "" [], connectToServer )
>> filters : Msg -> Model -> States Model Msgfilters msg model =
>> case msg of
>> ConnectTo rid -> States.enableAll |> States.sendCommand (ConnectToRoom
>> rid)
>> RoomConnected uid ->
>> States.enableAll
>> |> States.sendCommand FocusInputBox
>> |> States.sendCommand ScrollInputBox
>> |> States.sendUpdate JoinedRoom
>> ReceivedMessage msg -> States.enableAll
>> MsgOnInput msg -> States.enableAll |> States.sendUpdate (MsgTyped msg)
>> SendMsgDo -> States.enableAll |> States.sendCommand (SendMsgToRoom
>> model.msg)
>> update : UpdMsg -> Model -> Modelupdate msg model =
>> case msg of
>> JoinedRoom -> { model | msg="", msgs=[] }
>> MsgTyped msg -> { model | msg=msg }
>> MsgSent ->{ model | msg="" }
>> command : Msg -> Model -> Cmd Msgcommand msg model =
>> case msg of
>> FocusInputBox -> focusMessageInput
>> ScrollInputBox -> scrollScrollable Helpers Scrollable_Bottom
>> ".messenger-mesglist"
>> ConnectToRoom rid -> connectToRoom rid
>> SendMsgToRoom msg -> sendMsgToConnectedRoom msg
>>
>> ```
>>
>> Eh I am not sure I like it, at all actually. It basically converts the
>> usual helper functions into in-line in the case branches. It separates the
>> commands and update messages out, however all the original logic is now in
>> filter, which is basically just redistributing things (maybe it should be
>> called `router` at this point) and is growing larger then its purpose was
>> meant. Not sure of a good API yet...
>>
>>
>> On Thursday, August 11, 2016 at 12:32:44 PM UTC-6, Kasey Speakman wrote:
>>>
>>> I do like the signatures you mentioned:
>>>
>>> `update : Msg -> Model -> Model`
>>> `command: Msg -> Model -> Cmd Msg`
>>>
>>> However at that point, it no longer makes sense to use the same messages
>>> for effects and model updates. For example:
>>>
>>> `update: Evt -> Model -> Model`
>>> `command: Act -> Model -> Cmd Evt`
>>>
>>> Here, using Act for commands since Cmd is already taken. Evt for events
>>> (facts which have occurred).
>>>
>>> The question then becomes how `view` would be affected. Html Msg doesn't
>>> work there. If you did Html Act, then there is some boilerplate for actions
>>> that don't have effects:
>>>
>>> ```
>>> -- FUTURE READER: THIS IS AN EXAMPLE, NOT ACTUAL SYNTAX
>>> command : Act -> Model -> Cmd Evt
>>> command act model =
>>> case act of
>>> ChangeName name ->
>>> Cmd.fromEvt <| NameChanged name
>>>
>>> SubmitCustomerChanges customer ->
>>> Cmd.batch
>>> [ Cmd.fromEvt <| CustomerChangesSubmitted
>>> , callServerWithCustomer customer -- or model.Customer?
>>> ]
>>> ```
>>>
>>> An alternative is to allow Html to produce either Act or Evt. But that
>>> feels wrong. It would be too easy to try to use only one or the other for
>>> all the work.
>>>
>>> On Thursday, August 11, 2016 at 1:03:47 PM UTC-5, Kasey Speakman wrote:
>>>>
>>>> It was just something that bugged me when I first saw it in the
>>>> examples, and I just came to the realization of why. And I am wondering
>>>> out
>>>> loud what it look like if they were separate things.
>>>>
>>>> In your example case, you could make a 3rd command that did the issued
>>>> a new message InfoConnectIdAssigned to separate the model bit out. The
>>>> tradeoff is isolation of model changes vs increased boilerplate (issuing a
>>>> no-effect command). I think a shortcut method could be in order like
>>>> Cmd.none, but something like Cmd.fromMsg.
>>>>
>>>> On Thursday, August 11, 2016 at 12:53:16 PM UTC-5, OvermindDL1 wrote:
>>>>>
>>>>> I guess what you are proposing is separating a Message into a
>>>>> 'commands' and 'update' callbacks? That could work well too... I could
>>>>> add that as an option to my ProgramEx testing library (where I test with
>>>>> extra callbacks) if you want to play around with it? Do you propose that
>>>>> `update` would be `Msg -> Model -> Model` and `command` would be `Msg ->
>>>>> Model -> Cmd Msg, running in sequence of command before update? I can
>>>>> see
>>>>> a lot of them having a lot of duplicate code though, but I guess that
>>>>> could
>>>>> be removed from my currently testing `filters` callback to clean up the
>>>>> message into more pure stateful messages. Though if I did that I really
>>>>> think I would want to make two different message types, one for filters
>>>>> and
>>>>> one for consuming, however that may make larger API changes than would be
>>>>> easy... What precisely would you want it to look like?
>>>>>
>>>>>
>>>>> On Thursday, August 11, 2016 at 11:48:21 AM UTC-6, OvermindDL1 wrote:
>>>>>>
>>>>>> Just as an aside, but I quite often return a mutated model *and*
>>>>>> commands, such as this for the shortest example I am finding:
>>>>>> ```elm
>>>>>>
>>>>>> InfoConnect uid ->
>>>>>> ( { model | uid = uid }
>>>>>> , Cmd.batch
>>>>>> [ connect_roomlist 0
>>>>>> , connect_roomlist uid
>>>>>> ]
>>>>>> )
>>>>>>
>>>>>> ```
>>>>>> So when we connected to the server, got an InfoConnect message back
>>>>>> with the unique ID of the user, they then are allowed to connect to both
>>>>>> the public and their personal room lists, so I submit those connection
>>>>>> requests.
>>>>>>
>>>>>>
>>>>>> On Thursday, August 11, 2016 at 11:37:48 AM UTC-6, Kasey Speakman
>>>>>> wrote:
>>>>>>>
>>>>>>> Hi all,
>>>>>>>
>>>>>>> I'm getting to know Elm. I recently read this article
>>>>>>> <http://marcosh.github.io/post/2016/07/09/elm-event-sourcing.html>
>>>>>>> about event sourcing in Elm. Essentially, the time-traveling debugger
>>>>>>> is
>>>>>>> event sourcing. But it's a pattern that could be used in an app for
>>>>>>> other
>>>>>>> great things. (Lots of literature on that in the internet. One
>>>>>>> particular
>>>>>>> interest of mine is producing a complete failing use case from live
>>>>>>> running
>>>>>>> app -- it's just all the events. Obviously wouldn't work for real-time
>>>>>>> apps... too many events... but for most of mine it would.)
>>>>>>>
>>>>>>> However, one thing that is a hindrance (to the TTD as well) and that
>>>>>>> has always bothered me about the Elm examples is this signature.
>>>>>>>
>>>>>>> update : Msg -> Model -> (Model, Cmd Msg)
>>>>>>>
>>>>>>>
>>>>>>> Because an update returns both a Model and Cmd, for instance, the
>>>>>>> time-traveling debugger "...needs to tell the runtime not to perform
>>>>>>> any
>>>>>>> side-effects during replay to avoid these issues"[1]. An event-sourcing
>>>>>>> implementation would have to figure a way to do the same without
>>>>>>> runtime
>>>>>>> hooks.
>>>>>>>
>>>>>>> This part of the architecture mixes concerns by returning a model
>>>>>>> and effects. And usually (not always) you see each message returning
>>>>>>> one or
>>>>>>> the other, not both. From the docs:
>>>>>>>
>>>>>>> update : Msg -> Model -> (Model, Cmd Msg)
>>>>>>> update msg model =
>>>>>>> case msg of
>>>>>>> Roll ->
>>>>>>> (model, Random.generate NewFace (Random.int 1 6))
>>>>>>>
>>>>>>> NewFace newFace ->
>>>>>>> (Model newFace, Cmd.none)
>>>>>>>
>>>>>>>
>>>>>>> Here, Roll does an effect, but nothing with the model. NewFace
>>>>>>> returns a new model but no effects. You do have cases where you want to
>>>>>>> update a UI element when an outside effect happens, like activating a
>>>>>>> spinner when sending off an HTTP request. Those could still be modeled
>>>>>>> as
>>>>>>> two "Cmd"s. One that immediately returns a separate message affecting
>>>>>>> the
>>>>>>> model's spinner. And another to do the HTTP request.
>>>>>>>
>>>>>>> So it seems to me that there are really two concepts at work here.
>>>>>>> There are "events" which have happened and can be used completely
>>>>>>> deterministically, and there are commands which interface with the
>>>>>>> outside
>>>>>>> and may produce events.
>>>>>>>
>>>>>>> I think it's something worth pointing out and considering for the
>>>>>>> future. What do y'all think?
>>>>>>>
>>>>>>> Kasey
>>>>>>>
>>>>>>> [1] source <http://debug.elm-lang.org/>, section "How Elm makes
>>>>>>> this possible", subsection "Purity", last paragraph
>>>>>>>
>>>>>>
On Thursday, August 11, 2016 at 2:26:24 PM UTC-5, OvermindDL1 wrote:
>
> Can you supply a complete example of what you want it to look like
> (excepting view, we don't care about view right now).?
>
>
> On Thursday, August 11, 2016 at 1:25:38 PM UTC-6, OvermindDL1 wrote:
>>
>> In this case it is just filling in for 'something' that can dispatch some
>> messages to `update` and dispatch commands to `command`. I'm not sure how
>> to dispatch those without some dispatcher that is already acting like the
>> existing update...
>>
>>
>> On Thursday, August 11, 2016 at 1:21:42 PM UTC-6, Kasey Speakman wrote:
>>>
>>> I'm not sure I like it either. Seems like a lot of UI commands would
>>> just be auto-converted to events by `command`.
>>>
>>> However, in your example, I'm not sure what your filter code is about
>>> (my ignorance). If I am understanding it right, it's filling in for the
>>> case when you need to respond to an event and issue another command. This
>>> also seems like a separate concern from what I mentioned.
>>>
>>> On Thursday, August 11, 2016 at 1:53:15 PM UTC-5, OvermindDL1 wrote:
>>>>
>>>> Yeah I was playing with the idea for more message types though still
>>>> not convinced yet. Let's go hog-wild for a second to see how it would
>>>> look
>>>> with a fairly major overhaul and multiple message types though:
>>>> ```elm
>>>>
>>>> {-| Msg is used for global message dispatching, handled only by the
>>>> `filters` callback -}type Msg
>>>> = ConnectTo Int
>>>> | RoomConnected Int
>>>> | ReceivedMessage String
>>>> | MsgOnInput String
>>>> | SendMsgDo
>>>> type CmdMsg
>>>> = FocusInputBox
>>>> | ScrollInputBox
>>>> | ConnectToRoom Int
>>>> | SendMsgToRoom String
>>>> type UpdMsg
>>>> = JoinedRoom
>>>> | MsgTyped String
>>>> | MsgSent
>>>> type alias Model =
>>>> { uid : Int
>>>> , msg : String
>>>> , msgs : List String
>>>> }
>>>> init : ( Model, Cmd Msg )init = ( Model 0 "" [], connectToServer )
>>>> filters : Msg -> Model -> States Model Msgfilters msg model =
>>>> case msg of
>>>> ConnectTo rid -> States.enableAll |> States.sendCommand (ConnectToRoom
>>>> rid)
>>>> RoomConnected uid ->
>>>> States.enableAll
>>>> |> States.sendCommand FocusInputBox
>>>> |> States.sendCommand ScrollInputBox
>>>> |> States.sendUpdate JoinedRoom
>>>> ReceivedMessage msg -> States.enableAll
>>>> MsgOnInput msg -> States.enableAll |> States.sendUpdate (MsgTyped msg)
>>>> SendMsgDo -> States.enableAll |> States.sendCommand (SendMsgToRoom
>>>> model.msg)
>>>> update : UpdMsg -> Model -> Modelupdate msg model =
>>>> case msg of
>>>> JoinedRoom -> { model | msg="", msgs=[] }
>>>> MsgTyped msg -> { model | msg=msg }
>>>> MsgSent ->{ model | msg="" }
>>>> command : Msg -> Model -> Cmd Msgcommand msg model =
>>>> case msg of
>>>> FocusInputBox -> focusMessageInput
>>>> ScrollInputBox -> scrollScrollable Helpers Scrollable_Bottom
>>>> ".messenger-mesglist"
>>>> ConnectToRoom rid -> connectToRoom rid
>>>> SendMsgToRoom msg -> sendMsgToConnectedRoom msg
>>>>
>>>> ```
>>>>
>>>> Eh I am not sure I like it, at all actually. It basically converts the
>>>> usual helper functions into in-line in the case branches. It separates
>>>> the
>>>> commands and update messages out, however all the original logic is now in
>>>> filter, which is basically just redistributing things (maybe it should be
>>>> called `router` at this point) and is growing larger then its purpose was
>>>> meant. Not sure of a good API yet...
>>>>
>>>>
>>>> On Thursday, August 11, 2016 at 12:32:44 PM UTC-6, Kasey Speakman wrote:
>>>>>
>>>>> I do like the signatures you mentioned:
>>>>>
>>>>> `update : Msg -> Model -> Model`
>>>>> `command: Msg -> Model -> Cmd Msg`
>>>>>
>>>>> However at that point, it no longer makes sense to use the same
>>>>> messages for effects and model updates. For example:
>>>>>
>>>>> `update: Evt -> Model -> Model`
>>>>> `command: Act -> Model -> Cmd Evt`
>>>>>
>>>>> Here, using Act for commands since Cmd is already taken. Evt for
>>>>> events (facts which have occurred).
>>>>>
>>>>> The question then becomes how `view` would be affected. Html Msg
>>>>> doesn't work there. If you did Html Act, then there is some boilerplate
>>>>> for
>>>>> actions that don't have effects:
>>>>>
>>>>> ```
>>>>> -- FUTURE READER: THIS IS AN EXAMPLE, NOT ACTUAL SYNTAX
>>>>> command : Act -> Model -> Cmd Evt
>>>>> command act model =
>>>>> case act of
>>>>> ChangeName name ->
>>>>> Cmd.fromEvt <| NameChanged name
>>>>>
>>>>> SubmitCustomerChanges customer ->
>>>>> Cmd.batch
>>>>> [ Cmd.fromEvt <| CustomerChangesSubmitted
>>>>> , callServerWithCustomer customer -- or model.Customer?
>>>>> ]
>>>>> ```
>>>>>
>>>>> An alternative is to allow Html to produce either Act or Evt. But that
>>>>> feels wrong. It would be too easy to try to use only one or the other for
>>>>> all the work.
>>>>>
>>>>> On Thursday, August 11, 2016 at 1:03:47 PM UTC-5, Kasey Speakman wrote:
>>>>>>
>>>>>> It was just something that bugged me when I first saw it in the
>>>>>> examples, and I just came to the realization of why. And I am wondering
>>>>>> out
>>>>>> loud what it look like if they were separate things.
>>>>>>
>>>>>> In your example case, you could make a 3rd command that did the
>>>>>> issued a new message InfoConnectIdAssigned to separate the model bit
>>>>>> out.
>>>>>> The tradeoff is isolation of model changes vs increased boilerplate
>>>>>> (issuing a no-effect command). I think a shortcut method could be in
>>>>>> order
>>>>>> like Cmd.none, but something like Cmd.fromMsg.
>>>>>>
>>>>>> On Thursday, August 11, 2016 at 12:53:16 PM UTC-5, OvermindDL1 wrote:
>>>>>>>
>>>>>>> I guess what you are proposing is separating a Message into a
>>>>>>> 'commands' and 'update' callbacks? That could work well too... I
>>>>>>> could
>>>>>>> add that as an option to my ProgramEx testing library (where I test
>>>>>>> with
>>>>>>> extra callbacks) if you want to play around with it? Do you propose
>>>>>>> that
>>>>>>> `update` would be `Msg -> Model -> Model` and `command` would be `Msg
>>>>>>> ->
>>>>>>> Model -> Cmd Msg, running in sequence of command before update? I can
>>>>>>> see
>>>>>>> a lot of them having a lot of duplicate code though, but I guess that
>>>>>>> could
>>>>>>> be removed from my currently testing `filters` callback to clean up the
>>>>>>> message into more pure stateful messages. Though if I did that I
>>>>>>> really
>>>>>>> think I would want to make two different message types, one for filters
>>>>>>> and
>>>>>>> one for consuming, however that may make larger API changes than would
>>>>>>> be
>>>>>>> easy... What precisely would you want it to look like?
>>>>>>>
>>>>>>>
>>>>>>> On Thursday, August 11, 2016 at 11:48:21 AM UTC-6, OvermindDL1 wrote:
>>>>>>>>
>>>>>>>> Just as an aside, but I quite often return a mutated model *and*
>>>>>>>> commands, such as this for the shortest example I am finding:
>>>>>>>> ```elm
>>>>>>>>
>>>>>>>> InfoConnect uid ->
>>>>>>>> ( { model | uid = uid }
>>>>>>>> , Cmd.batch
>>>>>>>> [ connect_roomlist 0
>>>>>>>> , connect_roomlist uid
>>>>>>>> ]
>>>>>>>> )
>>>>>>>>
>>>>>>>> ```
>>>>>>>> So when we connected to the server, got an InfoConnect message back
>>>>>>>> with the unique ID of the user, they then are allowed to connect to
>>>>>>>> both
>>>>>>>> the public and their personal room lists, so I submit those connection
>>>>>>>> requests.
>>>>>>>>
>>>>>>>>
>>>>>>>> On Thursday, August 11, 2016 at 11:37:48 AM UTC-6, Kasey Speakman
>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>> Hi all,
>>>>>>>>>
>>>>>>>>> I'm getting to know Elm. I recently read this article
>>>>>>>>> <http://marcosh.github.io/post/2016/07/09/elm-event-sourcing.html>
>>>>>>>>> about event sourcing in Elm. Essentially, the time-traveling debugger
>>>>>>>>> is
>>>>>>>>> event sourcing. But it's a pattern that could be used in an app for
>>>>>>>>> other
>>>>>>>>> great things. (Lots of literature on that in the internet. One
>>>>>>>>> particular
>>>>>>>>> interest of mine is producing a complete failing use case from live
>>>>>>>>> running
>>>>>>>>> app -- it's just all the events. Obviously wouldn't work for
>>>>>>>>> real-time
>>>>>>>>> apps... too many events... but for most of mine it would.)
>>>>>>>>>
>>>>>>>>> However, one thing that is a hindrance (to the TTD as well) and
>>>>>>>>> that has always bothered me about the Elm examples is this signature.
>>>>>>>>>
>>>>>>>>> update : Msg -> Model -> (Model, Cmd Msg)
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Because an update returns both a Model and Cmd, for instance, the
>>>>>>>>> time-traveling debugger "...needs to tell the runtime not to perform
>>>>>>>>> any
>>>>>>>>> side-effects during replay to avoid these issues"[1]. An
>>>>>>>>> event-sourcing
>>>>>>>>> implementation would have to figure a way to do the same without
>>>>>>>>> runtime
>>>>>>>>> hooks.
>>>>>>>>>
>>>>>>>>> This part of the architecture mixes concerns by returning a model
>>>>>>>>> and effects. And usually (not always) you see each message returning
>>>>>>>>> one or
>>>>>>>>> the other, not both. From the docs:
>>>>>>>>>
>>>>>>>>> update : Msg -> Model -> (Model, Cmd Msg)
>>>>>>>>> update msg model =
>>>>>>>>> case msg of
>>>>>>>>> Roll ->
>>>>>>>>> (model, Random.generate NewFace (Random.int 1 6))
>>>>>>>>>
>>>>>>>>> NewFace newFace ->
>>>>>>>>> (Model newFace, Cmd.none)
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Here, Roll does an effect, but nothing with the model. NewFace
>>>>>>>>> returns a new model but no effects. You do have cases where you want
>>>>>>>>> to
>>>>>>>>> update a UI element when an outside effect happens, like activating a
>>>>>>>>> spinner when sending off an HTTP request. Those could still be
>>>>>>>>> modeled as
>>>>>>>>> two "Cmd"s. One that immediately returns a separate message affecting
>>>>>>>>> the
>>>>>>>>> model's spinner. And another to do the HTTP request.
>>>>>>>>>
>>>>>>>>> So it seems to me that there are really two concepts at work here.
>>>>>>>>> There are "events" which have happened and can be used completely
>>>>>>>>> deterministically, and there are commands which interface with the
>>>>>>>>> outside
>>>>>>>>> and may produce events.
>>>>>>>>>
>>>>>>>>> I think it's something worth pointing out and considering for the
>>>>>>>>> future. What do y'all think?
>>>>>>>>>
>>>>>>>>> Kasey
>>>>>>>>>
>>>>>>>>> [1] source <http://debug.elm-lang.org/>, section "How Elm makes
>>>>>>>>> this possible", subsection "Purity", last paragraph
>>>>>>>>>
>>>>>>>>
--
You received this message because you are subscribed to the Google Groups "Elm
Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.