Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upFeedback needed: Fractal `update` functions thanks to lenses #973
Comments
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
cedricss
Aug 19, 2018
Thanks for posting this!
A first quick reply to your initial problem. A "nested component" can have its own isolated update function. Consider this change on your example:
type alias Model =
{ notifications : Notifications.Model
, autoplay : Autoplay.Model
, location : Location.Model
}
type Msg
= NotificationsMsg Notifications.Msg
| AutoplayMsg Autoplay.Msg
| LocationMsg Location.Msg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NotificationsMsg notificationsMsg ->
let
( notificationsModel, notificationsCmd ) =
Notifications.update notificationsMsg model.notifications
in
( { model | notifications = notificationsModel }
, Cmd.Map NotificationsMsg notificationsCmd
)
...As you can see, the update function in the Notifications module is isolated and has its own Msg -> Model -> ( Model, Cmd Msg ) signature. Also, this module handles itself its messages (Notifications.Msg) and only them. The Main module does not need to know the internals of Notifications, and vice versa. Same principles can be applied for view, init and subscriptions. What do you think?
cedricss
commented
Aug 19, 2018
•
|
Thanks for posting this! A first quick reply to your initial problem. A "nested component" can have its own isolated type alias Model =
{ notifications : Notifications.Model
, autoplay : Autoplay.Model
, location : Location.Model
}
type Msg
= NotificationsMsg Notifications.Msg
| AutoplayMsg Autoplay.Msg
| LocationMsg Location.Msg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NotificationsMsg notificationsMsg ->
let
( notificationsModel, notificationsCmd ) =
Notifications.update notificationsMsg model.notifications
in
( { model | notifications = notificationsModel }
, Cmd.Map NotificationsMsg notificationsCmd
)
...As you can see, the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
avh4
Aug 19, 2018
Member
Hello, this type of discussion is suited for https://discourse.elm-lang.org (whereas github issues are used by Elm only for specific bugs that will be fixed) -- If you want to discuss further, please move the discussion there, thanks!
|
Hello, this type of discussion is suited for https://discourse.elm-lang.org (whereas github issues are used by Elm only for specific bugs that will be fixed) -- If you want to discuss further, please move the discussion there, thanks! |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
atomrc
Aug 19, 2018
Oh sure @avh4 !! I didn't realize there was a discourse for that. My apologies.
I created the topic there (pending at the moment) and will close this Issue now.
Sorry for the noise
atomrc
commented
Aug 19, 2018
|
Oh sure @avh4 !! I didn't realize there was a discourse for that. My apologies. |
atomrc
closed this
Aug 19, 2018
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
atomrc
Aug 19, 2018
@cedricss just replying here quickly as for the moment the thread is not yet active on discourse.
Thanks for this reply, it is interesting indeed. Quick question though, I am not sure I was clear about the fact that notifications location and autoplay are not 3 different components but 3 "instances" of the same component.
You piece of code would look like this, if I am not mistaking
type alias Model =
{ notifications : Checkbox.Model
, autoplay : Checkbox.Model
, location : Checkbox.Model
}
type Msg
= NotificationsMsg Checkbox.Msg
| AutoplayMsg Checkbox.Msg
| LocationMsg Checkbox.Msg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NotificationsMsg notificationsMsg ->
let
( notificationsModel, notificationsCmd ) =
Checkbox.update notificationsMsg model.notifications
in
( { model | notifications = notificationsModel }
, Cmd.Map NotificationsMsg notificationsCmd
)
...It would probably work, I'll need to try this out :)
I believe there is something I am missing though, how do you map checkbox messages to the Msg type? This is not something the parent has control over, Am I wrong?
atomrc
commented
Aug 19, 2018
|
@cedricss just replying here quickly as for the moment the thread is not yet active on Thanks for this reply, it is interesting indeed. Quick question though, I am not sure I was clear about the fact that You piece of code would look like this, if I am not mistaking type alias Model =
{ notifications : Checkbox.Model
, autoplay : Checkbox.Model
, location : Checkbox.Model
}
type Msg
= NotificationsMsg Checkbox.Msg
| AutoplayMsg Checkbox.Msg
| LocationMsg Checkbox.Msg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NotificationsMsg notificationsMsg ->
let
( notificationsModel, notificationsCmd ) =
Checkbox.update notificationsMsg model.notifications
in
( { model | notifications = notificationsModel }
, Cmd.Map NotificationsMsg notificationsCmd
)
...It would probably work, I'll need to try this out :) I believe there is something I am missing though, how do you map checkbox messages to the |
atomrc commentedAug 19, 2018
•
edited
Edited 2 times
-
atomrc
edited Aug 19, 2018 (most recent)
-
atomrc
edited Aug 19, 2018
-
atomrc
created Aug 19, 2018
Hi guys,
Quick disclaimer
I am very new to Elm and while reading
An introduction to Elmsomething bugged me. The fact that only views are shown as composable (cf. https://guide.elm-lang.org/reuse).I tried to find an elegant solution to let sub components/modules/functional slices of the app/... update their own model without having to know about the top level app model.
This is a very humble proposal and it's probably missing a lot of cases (for example
Commandare not handled in my current proposal). I am looking for feedback on this idea. There might be a way that I missed to do that already, so I am up for any feedback :)It's highly inspired of the
Cycle.jsway to do that (withcycle-onionifyand lenses https://github.com/staltz/cycle-onionify).In the rest of the issue I'll use the term
componentto talk about a functional slice of an app (something that knows how to render, that possesses the business logic to update its own state and that returns some effects (the former is not part of this proposal)).My problem: Components cannot define their own
updatefunctionSo far, what I see is that there can only be a single signature for any update function of the app
update : AppMessage -> AppModel -> AppModel.Whether you are building an
updatefunction for the root component or for a nested leaf component, the function will always take a fullAppModel.The problem is that leaf component then need to know about the structure of
AppModeland cannot be reused in another context (only their view can be reused).Concrete example: the
checkboxexampleLet's work on a concrete example to illustrate my proposal. The
checkboxexample is a good one I think http://elm-lang.org/examples/checkboxesThe model looks like this
The views are factorized with the
checkboxfunctionThe
updatemethod is defined by the parent that handles all the messages from thecheckboxviews.Lenses to the rescue
The idea is to be able to
selecta specific slice of the main model and let thecomponentwork on that slice independently. This way thecomponentcan work without knowing anything about the app structure.In order to achieve that, I came up with an
isolatefunction that will feed the component the right slice of the model and when an update occurs, update the parent model with the updated model of the component.Final result
Here is the current
updatefunction of the given exampleHere is the isolated version
This enable easy reuse of components at different level of the application without having to write a reducer every single time.
Limitations
This proposal is not yet a viable option for, as far as I know, two reasons:
Commandsare not handles (I might have a idea for that)checkboxwill react to a click on a single one of them (I have no idea yet how to fix that).What I am looking for?
Pretty much anything from "this is a super bad idea because of this, this and that" or "this is not the elm philosophy at all" to "Good idea, I might have some idea to fix the limitations" or "Well, is already possible with this solution".
Working on this was super interesting anyway and I really had a great time working on this, so even if this is a bad idea, my time was not lost at all ;)
BTW: really❤️ the language!!