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.
