I'm not sure: I don't think I'll be able to define an init function like
the following if I don't also have the "field addition" changes reverted:
init : a -> Model a
init old =
{ old | newField = 4 }
On Tuesday, August 23, 2016 at 10:29:59 AM UTC+2, Janis Voigtländer wrote:
>
> 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]
> <javascript:>>:
>
>> Greetings!
>>
>> I originally posted this on
>> https://github.com/elm-lang/elm-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] <javascript:>.
>> 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.