This suggests a generalization to the Elm update/view architecture pattern. 
The functions stay the same but the signatures change:

module Component exposing (InternalState, ExternalState, InternalMsg, init, 
update, view)

type InternalState = ... -- the purely internal information for the 
component

type ExternalState = ... -- the external value for the component (e.g., the 
value of a slider)

type InternalMsg = ... -- the messages sent to update the internal state

init : InternalState -- initial internal state based on an initial external 
state

update : InternalMsg -> InternalState -> (InternalState, Cmd InternalMsg) 
-- respond to internal messages

view : (ExternalState -> msg) -> (InternalMsg -> msg) -> ExternalState -> 
InternalState -> Html msg -- display

A further generalization would provide a standard way to allow the internal 
state to respond to external state changes.

The standard Elm architecture would be the special case where there was no 
external state. The slider example would be the case where there is no 
internal state. Evan's sorted table example would be a case where the 
internal state is simple enough that we can dispense with an update 
function for sorted tables and the table has no capacity to update the 
external state.

A lot of what these discussions around componentization seem to call for is 
more detail on how to stretch and contract the Elm architecture adding 
parameters in some cases and simplifying pieces away in others.

Mark

On Thursday, August 25, 2016 at 9:04:32 AM UTC-7, Richard Feldman wrote:
>
> Thanks! So let's start by talking about Slider for a sec.
>
>       SliderMsg (Slider.ChangeValue newSpeed) ->
>         { model | profile = newInternal, shared = Trip.setAverageSpeed 
> model.shared newSpeed }
>
> Because this code compiles, and it only covers the ChangeValue 
> constructor, then assuming speed is an Int, I can infer that Slider's Msg 
> looks like this:
>
> type Msg = ChangeValue Int
>
> A good question at this point is: *how much of Slider's state must be 
> synchronized *with other state in the application?
>
> For example, if Slider had some sort of visual flourish like "every time 
> you drag it, it changes color," that state would not need any 
> synchronization with other parts of the app. The slider would change color 
> when you dragged it, and the rest of the application would neither know nor 
> care.
>
> The Slider's value is not independent like this. If you put a slider on a 
> page and its value changes, but no other code ever finds out that its value 
> changed, then the slider is useless! Its value needs to be synchronized 
> with other state in order to be useful.
>
> Since Slider has no other state besides this value, then Slider's state is 
> *100% 
> synchronized* with other state. In a case like this, you have two options 
> for keeping it in synchronized:
>
> 1. Give Slider its own Msg, and translate between its Msg and its caller's 
> Msg
> 2. Do not give Slider its own Msg, just have its view accept a Msg 
> *constructor* as an argument.
>
> The second option is simpler. Here's how that would look:
>
>
> viewSlider : (value -> msg) -> value -> Html msg
>
>
> Now we can change Profile's Msg to this:
>
> type Msg =
>   WaypointConfMsg Int WaypointConf.Msg
>   | *SetSpeed Int*
>
>
> ...and render the slider like this:
>
> viewSlider SetSpeed currentSpeed
>
> So now Slider no longer has its own Msg, it just says "hey caller, tell me 
> how to wrap my values in your Msg type, whatever that might be, and we're 
> cool." Now instead of Slider being a Model, a Msg, an update, and a 
> view...it's just a view! :)
>
> Let's look at Waypoint next:
>
> WaypointConfMsg idx msg' ->
>         case msg' of
>           WaypointConf.ChangeDateOrTime _ ->
>             { model | profile = newInternal, shared = Trip.changeOpDate 
> model.shared idx <| getDate idx model newInternal }
>           WaypointConf.SameDateAndTime _ ->
>             { model | profile = newInternal }
>
> Like Slider, its state is still *100% synchronized*, meaning it also 
> doesn't need its own Msg. We can convert Waypoint into a view function 
> (which accepts a "here's how to wrap things as a Msg" function just like 
> Slider did—or maybe two of those functions, since there are two things 
> Waypoint can change), at which point we no longer need a Waypoint Msg, 
> Model, or update. Only a view.
>
> Finally, since Profile only has Waypoint and Slider inside it, and since 
> Profile has no independent state to track either, we can give it exactly 
> the same treatment as what we did for Waypoint and Slider.
>
> To summarize how I'd recommend organizing things:
>
>
> *Current*
>
> WaypointConf - Model, Msg, update, view
> Slider - Model, Msg, update, view
>
> Profile - Model, Msg, update, view
>
> App - Model, Msg, update, view
>
>
> *Proposed*
>
> WaypointConf - view
> Slider - view
> Profile - view
>
> App - Model, Msg, update, view
>
>
> Hope that helps! :)
>

-- 
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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to