Discussion:
[elm-discuss] Elm with one message
Vlad GURDIGA
2017-08-24 07:10:07 UTC
Permalink
Hey Elm-guys and Elm-gals! 👋

I have this toy-project
<https://github.com/gurdiga/xo.elm/tree/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed>
where I’m getting my feet wet with Elm, and I’ve found an approach to
compose view function that’s a bit different than what The Elm Architecture
recommends. 🀓

Because I found message transformation (mapping
<http://package.elm-lang.org/packages/elm-lang/html/2.0.0/Html#map>)
between nested components to be counterintuitive, I left out messages, for
the most part. I only have one
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Main.elm#L33-L41>
for the whole app: 🙃

type Msg
= Update Model (Cmd Msg) (Sub Msg)

update : Msg -> Model -> ( Model, Cmd Msg )update msg model =
case msg of
Update model cmd sub ->
( { model | subscription = sub }, cmd )


Only the top component references it
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Main.elm#L64>,
and from then on I use callback-style functions
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Dosar.elm#L31-L42>
for wiring up the nested components:

view : Dosar -> (Dosar -> Cmd msg -> Sub msg -> msg) -> Html msgview (Dosar data) callback =
let
c data =
callback (Dosar data)
in
div []
[ h1 [] [ text "Dosar nou" ]
, Temei.view data.temei (\v -> c { data | temei = v })
, DocumentExecutoriu.view data.documentExecutoriu (\v -> c { data | documentExecutoriu = v } Cmd.none Sub.none)
, Actiune.view data.actiune (\v -> c { data | actiune = v })
]


and also for event handlers
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Widgets/Fields.elm#L27-L34>
:

unlabeledTextField : String -> (String -> msg) -> List (Html msg)unlabeledTextField defaultValue callback =
[ input
[ value defaultValue
, onInput callback
]
[]
]


It seems to work well so far, and coming from JS I find this style a bit
more familiar conceptually: I only have functions and values. 🀓

I am at about 184K of Elm now, and I’m wondering if anyone else have tried
to do it this way and maybe found some gotchas that I should be aware of.
In particular, with regards to how the code compiles to JS and maybe other
Elm’s inner workings. 🀔

Cheers! 🀠

(NOTE: If you want to browse the code, please use this
revision: https://github.com/gurdiga/xo.elm/tree/9fe9bd1. After that I’m
bringing in elm-mdl
<http://package.elm-lang.org/packages/debois/elm-mdl/8.1.0>, and the code
will probably get too noisy for the purpose of this conversation. 🙂)
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Peter Damoc
2017-08-24 07:47:19 UTC
Permalink
On of the key recommendations of Elm Architecture is to have your messages
be just data.

Your approach is somewhat similar to the approach of elm-sortable-table
(except for the Cmds and Subs).

Without using Cmds and/or Subs it is an interesting approach if you use it
for managing very small bits of state (the open/close status of a dropdown)
but I'm afraid that it might bring performance problems if you use it for
the entirety of the app.
You basically have to computer all the possible future states of the app.
Post by Vlad GURDIGA
Hey Elm-guys and Elm-gals! 👋
I have this toy-project
<https://github.com/gurdiga/xo.elm/tree/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed>
where I’m getting my feet wet with Elm, and I’ve found an approach to
compose view function that’s a bit different than what The Elm Architecture
recommends. 🀓
Because I found message transformation (mapping
<http://package.elm-lang.org/packages/elm-lang/html/2.0.0/Html#map>)
between nested components to be counterintuitive, I left out messages, for
the most part. I only have one
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Main.elm#L33-L41>
for the whole app: 🙃
type Msg
= Update Model (Cmd Msg) (Sub Msg)
update : Msg -> Model -> ( Model, Cmd Msg )update msg model =
case msg of
Update model cmd sub ->
( { model | subscription = sub }, cmd )
Only the top component references it
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Main.elm#L64>,
and from then on I use callback-style functions
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Dosar.elm#L31-L42>
view : Dosar -> (Dosar -> Cmd msg -> Sub msg -> msg) -> Html msgview (Dosar data) callback =
let
c data =
callback (Dosar data)
in
div []
[ h1 [] [ text "Dosar nou" ]
, Temei.view data.temei (\v -> c { data | temei = v })
, DocumentExecutoriu.view data.documentExecutoriu (\v -> c { data | documentExecutoriu = v } Cmd.none Sub.none)
, Actiune.view data.actiune (\v -> c { data | actiune = v })
]
and also for event handlers
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Widgets/Fields.elm#L27-L34>
unlabeledTextField : String -> (String -> msg) -> List (Html msg)unlabeledTextField defaultValue callback =
[ input
[ value defaultValue
, onInput callback
]
[]
]
It seems to work well so far, and coming from JS I find this style a bit
more familiar conceptually: I only have functions and values. 🀓
I am at about 184K of Elm now, and I’m wondering if anyone else have tried
to do it this way and maybe found some gotchas that I should be aware of.
In particular, with regards to how the code compiles to JS and maybe other
Elm’s inner workings. 🀔
Cheers! 🀠
https://github.com/gurdiga/xo.elm/tree/9fe9bd1. After that I’m bringing
in elm-mdl <http://package.elm-lang.org/packages/debois/elm-mdl/8.1.0>,
and the code will probably get too noisy for the purpose of this
conversation. 🙂)
--
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
For more options, visit https://groups.google.com/d/optout.
--
There is NO FATE, we are the creators.
blog: http://damoc.ro/
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
'Rupert Smith' via Elm Discuss
2017-08-24 15:57:35 UTC
Permalink
Post by Peter Damoc
Without using Cmds and/or Subs it is an interesting approach if you use
it for managing very small bits of state (the open/close status of a
dropdown) but I'm afraid that it might bring performance problems if you
use it for the entirety of the app.
You basically have to computer all the possible future states of the app.
Not so sure about that, as the callbacks seem to only be invoked in
continuations?:

Temei.view data.temei (\v -> c { data | temei = v })
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Mark Hamburg
2017-08-24 16:18:38 UTC
Permalink
Ignoring the commands and subscriptions part, I think this could be summarized as being built around having any of the things that would send a message instead send a new state. This eliminates the level of indirection of wrapping the desire to change the state into a message and then unwrapping the message to actually do the state change. It is entirely based on data and hence should be compatible with message recording for the debugger. On the other hand, the messages will lack semantic information. (One could work around that by adding a logging text field that was set when building the update message but ignored when applying it.)

Where this would seem to fall down is commands and subscriptions. Those are going to produce asynchronous results and would risk baking in a stale model state. This is particularly true for commands. Imagine, for example, wiring up a tick command that will increment a tick field in the model after five seconds. Since the command will be constructed to send the single "set the state" message and since the only model it will have access to is the one that existed when the command was constructed, it basically becomes a "reset the state to where it was five seconds ago" command. If subscriptions really do get rebuilt after every cycle of the update loop, then this isn't a problem for them but the closure construction for each subscription is an expense. If they don't get rebuilt — and the code in the initial posting suggests that instead we're caching a subscriptions value — then as with commands, it would seem easy to end up with code that resurrects stale states.

Mark
Post by Vlad GURDIGA
Without using Cmds and/or Subs it is an interesting approach if you use it for managing very small bits of state (the open/close status of a dropdown) but I'm afraid that it might bring performance problems if you use it for the entirety of the app.
You basically have to computer all the possible future states of the app.
Temei.view data.temei (\v -> c { data | temei = v })
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
For more options, visit https://groups.google.com/d/optout.
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Michael Jones
2017-08-25 08:42:54 UTC
Permalink
I can second Mark's point. At my company we had a little experiment where
chunks of state where sent in the messages. Not the whole model but not a
single unit either. It seemed really promising for a moment and quite clean
but then we started getting bugs which were very hard to understand. Turned
out to be exactly what Mark suggested. Between messages and commands we'd
have multiple copies of the same chunk of state in flight, each based on
some original value but each ignorant of the changes that the other were
representing. The end result was to just get the last state and lose
anything associated with other messages or commands happening around the
same time.

We moved back to the standard strategy and all is well.

Michael
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-08-27 08:38:46 UTC
Permalink
Hey Michael! 👋

Thank you for chipping in! 🀓
Post by Michael Jones
I can second Mark's point. At my company we had a little experiment where
chunks of state where sent in the messages. Not the whole model but not a
single unit either. It seemed really promising for a moment and quite clean
but then we started getting bugs which were very hard to understand. Turned
out to be exactly what Mark suggested. Between messages and commands we'd
have multiple copies of the same chunk of state in flight, each based on
some original value but each ignorant of the changes that the other were
representing. The end result was to just get the last state and lose
anything associated with other messages or commands happening around the
same time.
We moved back to the standard strategy and all is well.
So — just to check my understanding — the standard strategy is to only send
enough bits of data with the message to describe the change that the update
function needed to carry on, an not the actual changed bits of state. Am I
close? 🀔
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-08-27 08:33:15 UTC
Permalink
Hey Mark! 👋

Thank you for taking time to look into this! 🀓
Post by Mark Hamburg
Ignoring the commands and subscriptions part, I think this could be
summarized as being built around having any of the things that would send a
message instead send a new state. This eliminates the level of indirection
of wrapping the desire to change the state into a message and then
unwrapping the message to actually do the state change.
I haven’t though of it in such a well-formalized manner, but yes, that
sounds like it: I eliminate one level of indirection. Or at least at some
level, because at the very top there is still a message that triggers the
update, even if it’s a super-generic one. 🀔
Post by Mark Hamburg
It is entirely based on data and hence should be compatible with message
recording for the debugger.
This looks about right too, as I mentioned in my response to Robin
<https://groups.google.com/d/msg/elm-discuss/ta64y4JBMc4/kZK7DqQxCwAJ>, the
debugger does as good of a job as it can with what I’m giving it. 👜
Post by Mark Hamburg
On the other hand, the messages will lack semantic information. (One could
work around that by adding a logging text field that was set when building
the update message but ignored when applying it.)
Yeah, that’s true. As I’ve mentioned
<https://groups.google.com/d/msg/elm-discuss/ta64y4JBMc4/kZK7DqQxCwAJ>, the
reason why I haven’t sensed this problem is because I’m not using the Elm
Reactor, because I don’t know how to get ports playing inside it. 😑
Post by Mark Hamburg
Where this would seem to fall down is commands and subscriptions. Those
are going to produce asynchronous results and would risk baking in a stale
model state. This is particularly true for commands. Imagine, for example,
wiring up a tick command that will increment a tick field in the model
after five seconds. Since the command will be constructed to send the
single "set the state" message and since the only model it will have access
to is the one that existed when the command was constructed, it basically
becomes a "reset the state to where it was five seconds ago" command. If
subscriptions really do get rebuilt after every cycle of the update loop,
then this isn't a problem for them but the closure construction for each
subscription is an expense. If they don't get rebuilt — and the code in the
initial posting suggests that instead we're caching a subscriptions value —
then as with commands, it would seem easy to end up with code that
resurrects stale states.
Hm
 I haven’t thought about this aspect of the commands+subscriptions duo,
although I think I understand the mechanics. 🀔

So far I think I’m in luck: I have one port (a in/out couple actually) for
talking to an external rich text editor, which I expect to be modal. The
user clicks a button, the editor opens in a modal dialog, edit-edit-edit,
then closes the editor and passes back to Elm, so it’s serialized which
means there should not be any other changes to the Elm state while the
editor is open. 😎

I’m guessing in time, there could come in more things like background
sync-up of some kind, and then it’ll be really good to keep in mind these
things that you’re mentioning about stale states. 🀓

Thank you! Really good catch! 👍
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Mark Hamburg
2017-08-28 01:01:12 UTC
Permalink
One other note: If you don't care about Reactor and the Debugger but do care about async problems, you could get almost the same structure by passing back Model -> Model functions instead of Model values. Doing so may cause you to receive a visit from the Elm thought police but it is entirely semantically viable within Elm (and it's what I could imagine a lot of other functional languages doing by default).

Mark
Post by Vlad GURDIGA
Hey Mark! 👋
Thank you for taking time to look into this! 🀓
Post by Mark Hamburg
Ignoring the commands and subscriptions part, I think this could be summarized as being built around having any of the things that would send a message instead send a new state. This eliminates the level of indirection of wrapping the desire to change the state into a message and then unwrapping the message to actually do the state change.
I haven’t though of it in such a well-formalized manner, but yes, that sounds like it: I eliminate one level of indirection. Or at least at some level, because at the very top there is still a message that triggers the update, even if it’s a super-generic one. 🀔
Post by Mark Hamburg
It is entirely based on data and hence should be compatible with message recording for the debugger.
This looks about right too, as I mentioned in my response to Robin, the debugger does as good of a job as it can with what I’m giving it. 👜
Post by Mark Hamburg
On the other hand, the messages will lack semantic information. (One could work around that by adding a logging text field that was set when building the update message but ignored when applying it.)
Yeah, that’s true. As I’ve mentioned, the reason why I haven’t sensed this problem is because I’m not using the Elm Reactor, because I don’t know how to get ports playing inside it. 😑
Post by Mark Hamburg
Where this would seem to fall down is commands and subscriptions. Those are going to produce asynchronous results and would risk baking in a stale model state. This is particularly true for commands. Imagine, for example, wiring up a tick command that will increment a tick field in the model after five seconds. Since the command will be constructed to send the single "set the state" message and since the only model it will have access to is the one that existed when the command was constructed, it basically becomes a "reset the state to where it was five seconds ago" command. If subscriptions really do get rebuilt after every cycle of the update loop, then this isn't a problem for them but the closure construction for each subscription is an expense. If they don't get rebuilt — and the code in the initial posting suggests that instead we're caching a subscriptions value — then as with commands, it would seem easy to end up with code that resurrects stale states.
Hm
 I haven’t thought about this aspect of the commands+subscriptions duo, although I think I understand the mechanics. 🀔
So far I think I’m in luck: I have one port (a in/out couple actually) for talking to an external rich text editor, which I expect to be modal. The user clicks a button, the editor opens in a modal dialog, edit-edit-edit, then closes the editor and passes back to Elm, so it’s serialized which means there should not be any other changes to the Elm state while the editor is open. 😎
I’m guessing in time, there could come in more things like background sync-up of some kind, and then it’ll be really good to keep in mind these things that you’re mentioning about stale states. 🀓
Thank you! Really good catch! 👍
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
For more options, visit https://groups.google.com/d/optout.
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-08-31 18:57:24 UTC
Permalink
Post by Mark Hamburg
One other note: If you don't care about Reactor and the Debugger but do
care about async problems, you could get almost the same structure by
passing back Model -> Model functions instead of Model values.
Sorry Mark, this sounds cool, but I’m kind of a slow thinker (generally) —
could you give some pseudo-code to show an example of what you mean? 😊
Post by Mark Hamburg
Doing so may cause you to receive a visit from the Elm thought police but
it is entirely semantically viable within Elm (and it's what I could
imagine a lot of other functional languages doing by default).
Mark
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Mark Hamburg
2017-08-31 19:29:13 UTC
Permalink
Your previous update structure was essentially (ignoring commands and
subscriptions)

type Msg
= Set Model

update : Msg -> Model -> Model
update (Set newModel) model =
newModel


used as something like:

... onClick (Set { model | counter = model.counter + 1 }) ...


The revised approach looks like:

type Msg
= Apply (Model -> Model)

update : Msg -> Model -> Model
update (Apply fn) model =
fn model


used as:

... onClick (Apply (\model -> { model | counter = model.counter + 1 })) ...


The point is that we avoid capturing the model value in the message being
routed around so that we can interleave this message with other
asynchronous responses.

Again, as noted in my earlier message, this is not the Elm way and will be
unfriendly to tools like the debugger but it is valid Elm. It is somewhat
more complicated than the original version but still avoids the level of
indirection associated with messages. It is also more efficient than the
original form in that it at most constructs new function closures rather
than constructing entire new model values for each possible action in the
UX.

Mark

P.S. I was tempted to also include this form in the example:

... onClick (Apply incrementCounter) ...

incrementCounter : Model -> Model
incrementCounter model =
{ model | counter = model.counter + 1 }

But if one did that, one might as well have an IncrementCounter message and
handle it in the update function.
Post by Vlad GURDIGA
Post by Mark Hamburg
One other note: If you don't care about Reactor and the Debugger but do
care about async problems, you could get almost the same structure by
passing back Model -> Model functions instead of Model values.
Sorry Mark, this sounds cool, but I’m kind of a slow thinker (generally) —
could you give some pseudo-code to show an example of what you mean? 😊
Post by Mark Hamburg
Doing so may cause you to receive a visit from the Elm thought police but
it is entirely semantically viable within Elm (and it's what I could
imagine a lot of other functional languages doing by default).
Mark
--
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
For more options, visit https://groups.google.com/d/optout.
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-09-01 06:38:31 UTC
Permalink
Oh wow! This looks super cool! 🀓


and it seems this would also cover for the issue that Peter mentioned
<https://groups.google.com/d/msg/elm-discuss/ta64y4JBMc4/Y8PEUFDPCgAJ>:
this gives me the ability to have a real onClick callback without
pre-computing the expected-after-click state! 🀠 — Right Peter? 🀔

OK, this is good. I’m not sure if I want to completely move to this model
right away — although Elm makes it easy to refactor in any way imaginable
😉 — but it’s definitely useful to have it under my belt. 🀓

Thank you Mark! 🙂
Post by Mark Hamburg
Your previous update structure was essentially (ignoring commands and
subscriptions)
type Msg
= Set Model
update : Msg -> Model -> Model
update (Set newModel) model =
newModel
... onClick (Set { model | counter = model.counter + 1 }) ...
type Msg
= Apply (Model -> Model)
update : Msg -> Model -> Model
update (Apply fn) model =
fn model
... onClick (Apply (\model -> { model | counter = model.counter + 1 })) ...
The point is that we avoid capturing the model value in the message being
routed around so that we can interleave this message with other
asynchronous responses.
Again, as noted in my earlier message, this is not the Elm way and will be
unfriendly to tools like the debugger but it is valid Elm. It is somewhat
more complicated than the original version but still avoids the level of
indirection associated with messages. It is also more efficient than the
original form in that it at most constructs new function closures rather
than constructing entire new model values for each possible action in the
UX.
Mark
... onClick (Apply incrementCounter) ...
incrementCounter : Model -> Model
incrementCounter model =
{ model | counter = model.counter + 1 }
But if one did that, one might as well have an IncrementCounter message
and handle it in the update function.
Post by Vlad GURDIGA
Post by Mark Hamburg
One other note: If you don't care about Reactor and the Debugger but do
care about async problems, you could get almost the same structure by
passing back Model -> Model functions instead of Model values.
Sorry Mark, this sounds cool, but I’m kind of a slow thinker (generally)
— could you give some pseudo-code to show an example of what you mean? 😊
Post by Mark Hamburg
Doing so may cause you to receive a visit from the Elm thought police
but it is entirely semantically viable within Elm (and it's what I could
imagine a lot of other functional languages doing by default).
Mark
--
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
For more options, visit https://groups.google.com/d/optout.
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Peter Damoc
2017-09-01 06:45:11 UTC
Permalink
Post by Vlad GURDIGA
Oh wow! This looks super cool! 🀓

and it seems this would also cover for the issue that Peter mentioned
this gives me the ability to have a real onClick callback without
pre-computing the expected-after-click state! 🀠 — Right Peter? 🀔
As far as I understand things, this breaks the debugger because instead of
data, the messages contain functions.
The recommendation is to keep Models and Msg as pure data.
--
There is NO FATE, we are the creators.
blog: http://damoc.ro/
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Michael Jones
2017-09-01 08:08:25 UTC
Permalink
"The recommendation is to keep Models and Msg as pure data."

Sound advice. I think that one trouble with passing functions around in
messages is that as they get more complex you stand a bigger & bigger
chance of closing over some other state and then you're back to the problem
of passing stale chunks of state around in your messages and it would be
enormously tricky to track down.
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-09-01 09:35:04 UTC
Permalink
Hm
 OK, noted. 🀔
Post by Michael Jones
"The recommendation is to keep Models and Msg as pure data."
Sound advice. I think that one trouble with passing functions around in
messages is that as they get more complex you stand a bigger & bigger
chance of closing over some other state and then you're back to the problem
of passing stale chunks of state around in your messages and it would be
enormously tricky to track down.
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-09-05 07:02:28 UTC
Permalink
So
 🀔

I’m adding adding a couple of buttons this morning, and I feel I’m worried
about that thing that Peter mentioned
<https://groups.google.com/d/msg/elm-discuss/ta64y4JBMc4/Y8PEUFDPCgAJ>:
Whenever I have an onClick with a callback instead of message I basically
pre-compute the after-click state. I think I understand the issue, and I’d
like to do it The Right Way®. 😐

In my app I have a large data entry form where initial fields determine
which subsequent fields will be. It’s my understanding that every change to
the model should be its own message. So far I have 50 fields in total (and
there will probably be more) — does this mean that I need to have 50 things
in my Msg? Or am I seeing it wrong? 🀓
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Peter Damoc
2017-09-05 08:11:29 UTC
Permalink
Post by Vlad GURDIGA
In my app I have a large data entry form where initial fields determine
which subsequent fields will be. It’s my understanding that every change to
the model should be its own message. So far I have 50 fields in total (and
there will probably be more) — does this mean that I need to have 50 things
in my Msg? Or am I seeing it wrong? 🀓
Yes, you need to handle all 50 things in your Msg but you don't need to
have all 50 tags in the main Msg.

You can handle things in chunks.
Here is the Forms example altered to show one way to approach this:
https://ellie-app.com/4dhSNYfnrqya1/0

Here is another way to structure that code:
https://ellie-app.com/4dhSNYfnrqya1/1

And yet another way to structure it:
https://ellie-app.com/4dhSNYfnrqya1/3

What might be appropriate to do in your case depends on the shape of the
data. If the data can be split in smaller records that make sense maybe you
should split it.
If the data cannot be split and just needs to be one large record, then
maybe you can split just the updates in some manner that makes sense to you
and keep passing the same big record to all the update functions (second
link from above)

Needless to say that what you have above is only one section extracted.
With multiple sections you will just have one root Msg for each section and
update functions for each section.
--
There is NO FATE, we are the creators.
blog: http://damoc.ro/
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-09-06 07:01:28 UTC
Permalink
Thank you Peter! 🀓

The third option that you proposed
<https://www.google.com/url?q=https%3A%2F%2Fellie-app.com%2F4dhSNYfnrqya1%2F3&sa=D&sntz=1&usg=AFQjCNHjxIoLswqMt-8OuJxiM1EiswzmlQ>
seems to fit nicer with the shape of my data: multiple nested records. I’ve
tried it, and, I got something compiling! 😎
https://github.com/gurdiga/xo.elm/pull/1.

So now my child-module exposes additionally its Msg and its update
function, which the parent-module uses to wire it up in its own fabric.

Does this look close to what you had in mind? 🀔
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-09-09 08:41:43 UTC
Permalink
Hey everybody!

Looking back on this thread, it looks like there are two major issues that
were brought up: 🀔

1. unintended state pre-computation in case of onClick handlers;
2. accidental resurrection of the pieces of state captured in closures.

From my understanding, it seems like (2.) is not directly related to my
mostly-messageless approach I initially presented here, and can also happen
with the standard TEA, so it’s more like something to keep in mind than
something that is immediately fixable in my code. Noted. ✍

The (1.) though, seems directly related to my mostly-messageless approach.
As Peter pointed out, it will get more problematic as the state grows, and
I wanted to solve it. The solution that Peter proposed was to re-introduce
messages and update functions, which would have solved the problem by
moving the state computation to the update function, and so deferring it
until the click actually happens, as appropriate per TEA. I’ve tried that
<https://github.com/gurdiga/xo.elm/pull/1> (to best of my understanding),
but compared with the mostly-messageless approach, it seems like the amount
of indirection and code required by messages and update function does not
justify itself. 🀔

One other thought that crossed my mind during this conversation — regarding
the state pre-computation issue — was to have the onClick callback be an
actual function that can be invoked when the click happens. The thing
though, is that because onClick doesn’t have a value to yield, its callback
is basically a zero-argument function, and so is evaluated immediately —
which is actually the essence of the issue. 😐

I worked around that by re-implementing onClick
<https://github.com/gurdiga/xo.elm/blob/db9f6e55524d9a3e27e1d5f435349dbbe96657d2/src/Utils/MyHtmlEvents.elm#L8-L10> to
accept a one-argument function instead: 🀓

onClick : (String -> msg) -> Html.Attribute msgonClick callback =
Html.Events.on "click" (Json.Decode.map callback Html.Events.targetValue)



even though the callback argument (an empty string) ends up ignored
<https://github.com/gurdiga/xo.elm/blob/db9f6e55524d9a3e27e1d5f435349dbbe96657d2/src/Dosar/Actiune/IncheiereIntentare/RezultatIncercareConciliere/PartileNuAjungLaIntelegere/BunuriUrmarite.elm#L49>.
🀓

Problem solved! 😎 🙂
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Mark Hamburg
2017-09-09 21:31:09 UTC
Permalink
If you are going to go that route, then you might as well just send the
function to invoke back around through the message loop. Well, I guess that
breaks the debugger in a way that sending the new model through the loop
does not, but as noted previously sending the model has issues with async
responses.

Mark
Post by Vlad GURDIGA
Hey everybody!
Looking back on this thread, it looks like there are two major issues that
were brought up: 🀔
1. unintended state pre-computation in case of onClick handlers;
2. accidental resurrection of the pieces of state captured in closures.
From my understanding, it seems like (2.) is not directly related to my
mostly-messageless approach I initially presented here, and can also happen
with the standard TEA, so it’s more like something to keep in mind than
something that is immediately fixable in my code. Noted. ✍
The (1.) though, seems directly related to my mostly-messageless approach.
As Peter pointed out, it will get more problematic as the state grows, and
I wanted to solve it. The solution that Peter proposed was to re-introduce
messages and update functions, which would have solved the problem by
moving the state computation to the update function, and so deferring it
until the click actually happens, as appropriate per TEA. I’ve tried that
<https://github.com/gurdiga/xo.elm/pull/1> (to best of my understanding),
but compared with the mostly-messageless approach, it seems like the amount
of indirection and code required by messages and update function does not
justify itself. 🀔
One other thought that crossed my mind during this conversation —
regarding the state pre-computation issue — was to have the onClick
callback be an actual function that can be invoked when the click happens.
The thing though, is that because onClick doesn’t have a value to yield,
its callback is basically a zero-argument function, and so is evaluated
immediately — which is actually the essence of the issue. 😐
I worked around that by re-implementing onClick
<https://github.com/gurdiga/xo.elm/blob/db9f6e55524d9a3e27e1d5f435349dbbe96657d2/src/Utils/MyHtmlEvents.elm#L8-L10> to
accept a one-argument function instead: 🀓
onClick : (String -> msg) -> Html.Attribute msgonClick callback =
Html.Events.on "click" (Json.Decode.map callback Html.Events.targetValue)

even though the callback argument (an empty string) ends up ignored
<https://github.com/gurdiga/xo.elm/blob/db9f6e55524d9a3e27e1d5f435349dbbe96657d2/src/Dosar/Actiune/IncheiereIntentare/RezultatIncercareConciliere/PartileNuAjungLaIntelegere/BunuriUrmarite.elm#L49>.
🀓
Problem solved! 😎 🙂
--
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
For more options, visit https://groups.google.com/d/optout.
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-08-27 08:15:17 UTC
Permalink
Hey Peter! 👋

Thank you for looking into this thing! 🀓
Post by Peter Damoc
On of the key recommendations of Elm Architecture is to have your messages
be just data.
Your approach is somewhat similar to the approach of elm-sortable-table
(except for the Cmds and Subs).
Without using Cmds and/or Subs it is an interesting approach if you use
it for managing very small bits of state (the open/close status of a
dropdown) but I'm afraid that it might bring performance problems if you
use it for the entirety of the app.
Regarding “very small bits” — I think this idea was one of the goals of
going this way: any module represents one domain concept, and its view’s
callback function deals with a value of that domain concept’s type. If a
module refers to another domain concept, that concept has its separate
module with its type, and view, and callback function that only knows about
itself. (Well, ideally at least, for the most part, you know
 😉)
Post by Peter Damoc
You basically have to computer all the possible future states of the app.
Not sure what you mean here. Could you please expand, or maybe point to an
example in the code? 🀔
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Peter Damoc
2017-08-27 16:13:13 UTC
Permalink
Post by Peter Damoc
You basically have to computer all the possible future states of the app.
Not sure what you mean here. Could you please expand, or maybe point to an
example in the code? 🀔
Let's simply the problem space and imagine that you have 3 buttons that
when clicked do some serious computation on the model.

With a traditional approach you have the message sent by one of the buttons
and the specific computation done in response to the message.

With this approach, you would have to compute all 3 heavy computation and
send the new state with the message. If your main message is:

type Msg
= Update Model

then the 3 messages you will have to use on `onClick` would be equivalent
to:

div []
[ button [onClick (Update (doTheUpdateForMessageOne oldModel))] [text
"Do First Thing"]
, button [onClick (Update (doTheUpdateForMessageTwo oldModel))] [text
"Do Second Thing"]
, button [onClick (Update (doTheUpdateForMessageThree oldModel))] [text
"Do Third Thing"]
]

Or, more realistically, inside a child component with its own `update`,
have something like:

view onUpdate model =
let
toMsg msg = onUpdate (update msg model)
in
div []
[ button [onClick (toMsg First)] [text "Do First Thing"]
, button [onClick (toMsg Second)] [text "Do Second Thing"]
, button [onClick (toMsg Third)] [text "Do Third Thing"]
]

this means that by the time the view is rendered, all 3 future states of
the that component have been computed.
This is not a big issue if computing all future states is trivial (e.g. one
toggle state of some drawer) so it's fine for small widgets but if you
scale this to the level of the entire app, you might run into performance
issues.
--
There is NO FATE, we are the creators.
blog: http://damoc.ro/
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-08-31 18:54:33 UTC
Permalink
Ooooh! 
now I understand. 🀔

There was this question looping into the back of my mind regarding onClick
callbacks: “How do I get a zero-argument function in Elm?” and your
explanation clarifies it — there can’t be a zero-argument function in Elm!
Right? 😶

I’m surely curious about the philosophical implication of this, but even
without them I’m happy to have this clarified. 🀓

Thank you!
Post by Peter Damoc
Post by Peter Damoc
You basically have to computer all the possible future states of the app.
Not sure what you mean here. Could you please expand, or maybe point to
an example in the code? 🀔
Let's simply the problem space and imagine that you have 3 buttons that
when clicked do some serious computation on the model.
With a traditional approach you have the message sent by one of the
buttons and the specific computation done in response to the message.
With this approach, you would have to compute all 3 heavy computation and
type Msg
= Update Model
then the 3 messages you will have to use on `onClick` would be equivalent
div []
[ button [onClick (Update (doTheUpdateForMessageOne oldModel))] [text
"Do First Thing"]
, button [onClick (Update (doTheUpdateForMessageTwo oldModel))] [text
"Do Second Thing"]
, button [onClick (Update (doTheUpdateForMessageThree oldModel))]
[text "Do Third Thing"]
]
Or, more realistically, inside a child component with its own `update`,
view onUpdate model =
let
toMsg msg = onUpdate (update msg model)
in
div []
[ button [onClick (toMsg First)] [text "Do First Thing"]
, button [onClick (toMsg Second)] [text "Do Second Thing"]
, button [onClick (toMsg Third)] [text "Do Third Thing"]
]
this means that by the time the view is rendered, all 3 future states of
the that component have been computed.
This is not a big issue if computing all future states is trivial (e.g.
one toggle state of some drawer) so it's fine for small widgets but if you
scale this to the level of the entire app, you might run into performance
issues.
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Robin Heggelund Hansen
2017-08-24 08:31:20 UTC
Permalink
Won't this break the reactor/debugger? You can't really tell what sort of
messages is being passed around in your application with this model, if you
want to export message history, it would fail as well I believe.

I think a better way would be to not have nested components. Keep your
entire message hierarchy, and model structure, flat.
Post by Vlad GURDIGA
Hey Elm-guys and Elm-gals! 👋
I have this toy-project
<https://github.com/gurdiga/xo.elm/tree/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed>
where I’m getting my feet wet with Elm, and I’ve found an approach to
compose view function that’s a bit different than what The Elm Architecture
recommends. 🀓
Because I found message transformation (mapping
<http://package.elm-lang.org/packages/elm-lang/html/2.0.0/Html#map>)
between nested components to be counterintuitive, I left out messages, for
the most part. I only have one
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Main.elm#L33-L41>
for the whole app: 🙃
type Msg
= Update Model (Cmd Msg) (Sub Msg)
update : Msg -> Model -> ( Model, Cmd Msg )update msg model =
case msg of
Update model cmd sub ->
( { model | subscription = sub }, cmd )
Only the top component references it
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Main.elm#L64>,
and from then on I use callback-style functions
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Dosar.elm#L31-L42>
view : Dosar -> (Dosar -> Cmd msg -> Sub msg -> msg) -> Html msgview (Dosar data) callback =
let
c data =
callback (Dosar data)
in
div []
[ h1 [] [ text "Dosar nou" ]
, Temei.view data.temei (\v -> c { data | temei = v })
, DocumentExecutoriu.view data.documentExecutoriu (\v -> c { data | documentExecutoriu = v } Cmd.none Sub.none)
, Actiune.view data.actiune (\v -> c { data | actiune = v })
]
and also for event handlers
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/src/Widgets/Fields.elm#L27-L34>
unlabeledTextField : String -> (String -> msg) -> List (Html msg)unlabeledTextField defaultValue callback =
[ input
[ value defaultValue
, onInput callback
]
[]
]
It seems to work well so far, and coming from JS I find this style a bit
more familiar conceptually: I only have functions and values. 🀓
I am at about 184K of Elm now, and I’m wondering if anyone else have tried
to do it this way and maybe found some gotchas that I should be aware of.
In particular, with regards to how the code compiles to JS and maybe other
Elm’s inner workings. 🀔
Cheers! 🀠
https://github.com/gurdiga/xo.elm/tree/9fe9bd1. After that I’m bringing
in elm-mdl <http://package.elm-lang.org/packages/debois/elm-mdl/8.1.0>,
and the code will probably get too noisy for the purpose of this
conversation. 🙂)
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-08-27 08:16:08 UTC
Permalink
Hey Robin! 👋

Thank you for checking and thinking about my exploration here! 🙂

On Thursday, August 24, 2017 at 11:31:20 AM UTC+3, Robin Heggelund Hansen
Post by Robin Heggelund Hansen
Won't this break the reactor/debugger? You can't really tell what sort of
messages is being passed around in your application with this model, if you
want to export message history, it would fail as well I believe.
If by “break” you mean that I won’t be able to tell by looking at the
message history in the Debugger window, then you’re right: there is only
one kind of message, and you can’t really tell much just by looking at
that. 😶

The reason it wasn’t an issue for me is that I didn’t use the Debugger and
stopped using the Reactor as soon as I brought in ports. I don’t know how
to plug in the JS that I call through ports, without having a separate
index.html
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/index.html>
in which I plug the external library
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/index.html#L13>
, the bundle coming out of Elm
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/index.html#L14>,
and then the bits of glue code
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/index.html#L21-L22>
to tie them together. 😑

I’m wondering if anyone has experience with having ports working with the
Elm Reactor. 🀔
Post by Robin Heggelund Hansen
I think a better way would be to not have nested components. Keep your
entire message hierarchy, and model structure, flat.
Yeah, this is the default recommendation that I hear in the Elm community.
I’ve heard Richard Feldman mentioning that they also have a flat structure
for their production app at NoRedInk, but I think I just can’t imagine how
would that look like, and then, as I guess every other mortal, I fallback
to ways that I know. 🙃

One other reason why I went hierarchical is that I’m used to seeing domain
models as hierarchies, and in My Ideal World®✚ the code follows the domain
as close as possible. 😎

I’m definitely open to hearing, or — even better — *seeing* examples of
large apps that use a flat structure. 🀓
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Robin Heggelund Hansen
2017-08-28 13:28:03 UTC
Permalink
You can use the Elm debugger just fine with, say, webpack. Just pass in the
`debugger: true` flag and you're good to go.
Post by Vlad GURDIGA
Hey Robin! 👋
Thank you for checking and thinking about my exploration here! 🙂
On Thursday, August 24, 2017 at 11:31:20 AM UTC+3, Robin Heggelund Hansen
Post by Robin Heggelund Hansen
Won't this break the reactor/debugger? You can't really tell what sort of
messages is being passed around in your application with this model, if you
want to export message history, it would fail as well I believe.
If by “break” you mean that I won’t be able to tell by looking at the
message history in the Debugger window, then you’re right: there is only
one kind of message, and you can’t really tell much just by looking at
that. 😶
The reason it wasn’t an issue for me is that I didn’t use the Debugger and
stopped using the Reactor as soon as I brought in ports. I don’t know how
to plug in the JS that I call through ports, without having a separate
index.html
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/index.html>
in which I plug the external library
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/index.html#L13>
, the bundle coming out of Elm
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/index.html#L14>,
and then the bits of glue code
<https://github.com/gurdiga/xo.elm/blob/9fe9bd1b9f7cb13b6653ec55d9e873033e8930ed/index.html#L21-L22>
to tie them together. 😑
I’m wondering if anyone has experience with having ports working with the
Elm Reactor. 🀔
Post by Robin Heggelund Hansen
I think a better way would be to not have nested components. Keep your
entire message hierarchy, and model structure, flat.
Yeah, this is the default recommendation that I hear in the Elm community.
I’ve heard Richard Feldman mentioning that they also have a flat structure
for their production app at NoRedInk, but I think I just can’t imagine how
would that look like, and then, as I guess every other mortal, I fallback
to ways that I know. 🙃
One other reason why I went hierarchical is that I’m used to seeing domain
models as hierarchies, and in My Ideal World®✚ the code follows the domain
as close as possible. 😎
I’m definitely open to hearing, or — even better — *seeing* examples of
large apps that use a flat structure. 🀓
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Vlad GURDIGA
2017-08-31 19:01:12 UTC
Permalink
This would probably come in handy later, but for now I really enjoy having
elm-make as my only build step. 🀓

On Monday, August 28, 2017 at 4:28:03 PM UTC+3, Robin Heggelund Hansen
Post by Robin Heggelund Hansen
You can use the Elm debugger just fine with, say, webpack. Just pass in
the `debugger: true` flag and you're good to go.
--
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 elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...