Well, actually it would have been (swapped argument order compared to what
I wrote before):

init : a -> Model ainit old = Model 4 old

But the point stands, that you would only need the row "record constructors
that add fields" from
https://github.com/elm-lang/elm-platform/blob/master/upgrade-docs/0.16.md#updating-syntax
to be reverted.


2016-08-23 11:27 GMT+02:00 Janis Voigtländer <[email protected]>:

> You should be able to do that.
>
> If you have
>
> type alias Model a =
>    { a | newField : Int }
>
> then
>
> init : a -> Model ainit old = Model old 4
>
> is exactly equivalent (in pre-0.16 Elm) to
>
> init : a -> Model ainit old = { old | newField = 4 }
>
> So where’s the problem?
> ​
>
> 2016-08-23 11:10 GMT+02:00 Charles-Edouard Cady <
> [email protected]>:
>
>> 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/el
>>> m-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/el
>>>> m-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.
>>
>
>

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