I'd like to revisit @jvoigtlaender <https://github.com/jvoigtlaender> 's
suggestion here
(https://github.com/elm-lang/elm-compiler/issues/1308#issuecomment-194703200)
about re-introducing constructors for extensible records only.
I bumped into a case where I believe just including *constructors* would
have helped in achieving two of elm's core ideas, good errors and making
impossible states impossible. It happens in decoding:
Imagine a type:
```
type alias Person =
{ id : Int
, name : String
, address : Address
}
```
When we're decoding, we have nice insurance that all Person types have an
address. But the world is complicated, and sometimes Person's are homeless
and don't have an address :(.
We could represent this with Maybe Address, and decode a nil value from the
backend as a Nothing - thus representing homeless as `address = Nothing`.
But this isn't good because if the backed data is mistakenly nil, we have
no way to raise an error to validate that the data is correct.
We could add an additional field `{ hasHome : Bool }`. During decoding, we
could use `Result.map` to write a validator to ensure that when `hasHome =
True`, we decode to Err if address is nil value. But this opens us up to
the problem of having an invalid state: `hasHome == True && address ==
Nothing`.
Another solution, one which developers it seems tend towards, is using
extensible types to represent the states:
```
type alias PersonBase a =
{ a
| id : Int
, name : Int
}
type alias Person =
PersonBase { address : Address }
type alias HomelessPerson =
PersonBase {}
```
This enables us to give raise decoding errors, and to make impossible
states impossible. It becomes unwieldy very fast to start creating the
constructors for when the records grow in size.
Since this is something I ran into, I thought I'd bring it up, but also
reframe the conversation in terms of the best way to achieve the design
goals we strive towards.
On Tuesday, January 3, 2017 at 8:49:46 AM UTC-8, Leo Zhang wrote:
>
> Sorry to necropost, but this problem bit me recently. Here's my use case.
>
> I'm working on an client that displays albums, static images, and animated
> images. The backend (which is third-party, so I can't change it) sends has
> lots of redundancy in the data models it sends me, so my models look
> something like this:
>
> AnimatedImage is a superset of StaticImage is a superset of GalleryItem;
> Album is a superset of GalleryItem.
>
> The way I've got this worked out using extension is:
>
> type alias StaticImage = {
> -- static image fields here
> }
>
> type alias MakeAnimated staticImage = {
> staticImage |
> -- animated image fields here
> }
>
> type alias AnimatedImage = MakeAnimated StaticImage
>
> Unfortunately, I can't figure out how to do JSON decoding this way because
> there is no type constructor for AnimatedImage. I'm working around this by
> nesting instead (i.e. having AnimatedImage contain a StaticImage field
> which contains a GalleryItem field), but this quickly gets unwieldy because
> working with AnimatedImages still requires me to access GalleryItem
> properties (e.g. all gallery items have a "url" field) a lot and nesting
> makes my code more noisy without adding semantic meaning.
>
> On Friday, 18 March 2016 01:06:22 UTC-7, Magnus Rundberget wrote:
>>
>> I'm not sure I buy that as a convincing argument for not using nesting.
>>
>> I'm inferring (sorry if that's a strawman, but your post is a bit scarce
>> in terms of details) that you are saying that the view model(s) in elm
>> must map 1-1 with the backend requests ? That to me is a bit like saying
>> your backend model needs to map 1-1 with your database model isn't it ? I
>> think it's to be expected to have to do mapping between abstraction layers
>> and even more so between process boundaries.
>> Something like graphql might make that less the case for web apps, but I
>> still believe there will be some level of mapping regardless.
>>
>> I can't think of a single time I found me wanting to mirror the structure
>> of my ui model exactly like the structure of a large nested request,
>> modify it and then send back the modified stuff to the backend
>>
>> Maybe you could elaborate a little bit about your use case, to help me
>> understand why the ui would/could be structured like the document structure
>> from your backend documents ?
>>
>>
>> Just to be clear I'm not entirely convinced nesting is always the best
>> solution (especially when Elm seems to favor making it difficult/cumbersome
>> to perform nested record updates). However I haven't come up with a great
>> example where record extension(inheritance in my still highly oo influnced
>> brain) would clearly be better than nesting (aka composition?)
>>
>> cheers
>> - magnus
>>
>>
>>
>>
>>
>>
>>
>> On Friday, 18 March 2016 08:22:36 UTC+1, Kamil Durkiewicz wrote:
>>>
>>> Hi guys,
>>>
>>> I've got another case when nesting is worse than extension. It comes
>>> from our current project. We're building a client application for an
>>> existing backend (which we cannot change). The backend returns large and
>>> nested documents (dozens of fields on each level) for GET requests and
>>> accepts documents of the same structure in POST requests when you want to
>>> do an update. In such case, the client application need to use the same
>>> document structure, otherwise it would be really confusing and error-prone
>>> at request construction level.
>>>
>>
--
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.