Yes you're right, I hadn't seen it that way, thanks. On Tuesday, August 23, 2016 at 11:48:43 AM UTC+2, Janis Voigtländer wrote: > > 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] > <javascript:>>: > >> 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] >> <javascript:>>: >> >>> 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] >>>> >: >>>> >>>>> 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]. >>>>> 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] <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.
