Is it correct that your issue could be addressed by not bringing back
arbitrary record extension in expressions via the pre-0.16 syntax { ... |
... = ... }, but *only* bringing back constructor functions for extensible
records? That is, if from the table at https://github.com/elm-lang/
elm-platform/blob/master/upgrade-docs/0.16.md#updating-syntax only language
change in the row “record constructors that add fields” were reverted, but
not the language changes in the other rows?

Also relevant in this context, then: https://github.com/elm-lang/
elm-compiler/issues/1308.

2016-08-23 10:23 GMT+02:00 Charles-Edouard Cady <
[email protected]>:

> Greetings!
>
> I originally posted this on https://github.com/elm-lang/el
> m-compiler/issues/985 which, as Richard Feldman pointed out, is probably
> not the best place to put it. I'll follow Richard's suggestion & be a bit
> less abstract.
>
> I'm building an application to help ship captains create routes. My
> question is about the organization of the model of this application.
>
> The application currently has one page containing three widgets:
>
>    1. A map showing the routes
>    2. A table showing the performances (eg. fuel consumption,
>    duration...) of each route
>    3. A profile widget showing the speed profile on the selected route
>
>
> It is structured in three layers of decreasing size (and increasing
> versatility):
>
>
>    1. The top layer, providing synchronization between the widgets and
>    the layout of the page
>    2. The widget layer (all widgets are independent)
>    3. The elementary widget layer (containing sliders, date pickers...)
>    ie. reusable components
>
>
> The reusable components have their own model, which is independent from
> the rest of the application: their model is initialized by their containing
> widget.
> The widgets, however, must share some information (eg. the list of routes)
> but not all (for example, the profile widget couldn't care less which route
> is hovered in the table widget, but the map widget does) because it
> complicates refactoring (changing the model one widget impacts the others)
> and makes it difficult to reuse widgets (eg. use the profile widget in a
> context where I do not need/have the fuel consumption).
>
> Now in Elm's architecture tutorial, there are two extreme cases:
>
>    - The same model is shared by all widgets
>    - Each widget has its own independent model
>
>
> I find myself somewhere in between: part of the model is shared by all
> widgets and is application-independent (eg. the waypoints of each route),
> part of it is shared only by two widgets and mostly concerns the layout
> (eg. route hovering) and part of it is not shared (and should not be) (eg.
> the sliders' state).
>
> At the top-level (Page):
>
>
> type alias Model a =
>   { a
>   | routes : List Route
>   , hovered : Maybe Int
>   , selected : Maybe Int
>   , map : Map.InternalModel
>   , table : Table.InternalModel
>   , profile : Profile.InternalModel
>   }
>
> The Profile widget might use a part of this model:
>
> type alias Model a =
>   { a
>   | route : List Route
>   , selected : Maybe Int
>   , profile : InternalModel
>   }
>
> while the Table widget uses another:
>
> type alias Model a =
>   { a
>   | route : List Route
>   , selected : Maybe Int
>   , hovered : Maybe Int
>   , table : InternalModel
>   }
>
>
> Just like @rgrempel in https://github.com/elm-lang/elm-compiler/issues/985,
> I want each module also provides its init function to initialize its part
> of Page's model. With extensible records, I could simply do (eg. in Table
> ):
>
> init : a -> Model a
> init foo =
>   {foo | table = initInternal}
>
> and for Page I would have the very clean and composible chain:
>
> init : Model
> init =
>   {routes = []}
>   |> Table.init
>   |> Profile.init
>
> So the extensible records were an easy way for me to build composable
> applications. With the removal of this feature, the init function can no
> longer be type-parametrized. This is really important so let me emphasize a
> bit: *no extensible records means init must know the full record it
> operates on*.
>
> So I decided to use @rgrempel's strategy, but in his case where all parts
> of his model were independent. The only workaround I found is to do the
> following for Page (top-level):
>
> type alias Model a =
>   { a
>   | shared : Shared.Shared
>   , map : Map.InternalModel
>   , table : Table.InternalModel
>   , profile : Profile.InternalModel
>   }
>
> init : Model
> init =
>   { shared = Shared.init
>   , map = Map.init
>   , table = Table.init
>   , profile = Profile.init
>   }
>
> When you loose extensible records you have to put all shared parts in a
> Shared record, which essentially means you know in advance how your
> widget will be used.  For instance, the data shared by the Profile and
> the Table widgets is not the same as that used by the Profile and the Map
> widgets and if I add another widget, chances are I'll have to modify the
> Shared record. This makes me sad because it breaks separation of concern.
> With extensible records, I could simply add the fields I need to Page's
> model & in the specific widgets & they would simply be ignored by the other
> widgets. Whenever I modify what is shared, I'm modifying the Shared
> record that all widgets depend on &o if I decide to use eg. the Profile
> widget in another application, it will quickly become unmanageable.
>
> As previously stated, as soon as you define an init function, the record
> it returns (or operates on) can no longer be type-parametrized (i.e.
> extensible), which means that if init returns the shared part, Shared is
> no longer extensible. If Shared is not extensible & you want to include
> Page in a bigger application, Page's model will have to be completely
> independent from the other widgets' model at the same level, ie. it will
> not be able to share part of its model with the other widgets.
>
> With extensible records, you could apply the same pattern to any number of
> levels, but as soon as you put shared data in a shared field you're
> basically stating once and for all what is shared by all possible widgets:
> that information is only needed at the application level (at the Page
> level), but it dribbles down to all widgets (which shouldn't care whether
> they're being used in isolation or not).
>
> Sorry to ramble on about this, but it's been a thorn in my side for a long
> time now.
>
> I would really appreciate any thoughts on this.
>
> --
> 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.
>

-- 
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.

Reply via email to