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.