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.

Reply via email to