I came across extensible record code like the following:
type alias Editable r
= { r | author: String, created : Int }
I understand it’s saying "I’m creating a record type who can hold any fields so long as it has two particular fields author and created". However I have not seen the pipe operator being introduced in creating record type in the office document. It’s indeed explained as a way to update a record value though.
Is it some sort of hack or it has a general usage beyond the definition of extensible record?
Your understandment is correct - the | symbol is present on two different situations, meaning two different things. When defining a type alias it is used to represent an extensible record. But it can also mean “return this record with this update”. Those two usages are unrelated.
Sorry for my bad English expression. I mean how can a pipe operator be used in defining a record type? According to these well-known documents(e.g., offical guide, Beginning Elm), a pipe is used to updating a record value, not to compose a new record type.
So I’m wondering If I’ve missed some key concepts of Elm.
The syntax for extensible records and record update expressions have a similar meaning, but they do something different. It’s similar to how arguments exist for both types and functions.
I’m surprised that the official language guide does not mention extensible record types. I’m sure that must have been where I first read about them years ago, but I don’t see any mention of them now.
It actually looks like it used to be on this page, but was removed.
Really? It appeared and dispeared again? What happened? The extensible record seems an important concept when building large application. The Richard’s video has shown the importance in narrowing type. I love this concept.
Following Evan recommendation, I wouldn’t use them. The compiler has bugs dealing with them and it is recommended using record composition over extensible records. Also consider how you would reuse/compose decoders. For example:
type alias PersonalDetails a =
{ a
| name : String
, age : Int
}
type alias AccessData a =
{ a
| isAdmin : Bool
, isBanned : Bool
}
type Profile =
PersonalDetails (AccessData {})
VS
type alias PersonalDetails =
{ name : String
, age : Int
}
type alias AccessData =
{ isAdmin : Bool
, isBanned : Bool
}
type Profile =
{ details : PersonalDetails
, access : AccessData
}
PS: I would be surprised that they are removed in future releases of Elm.
Nothing wrong with using them in data modelling either - just don’t expect them to work like subtypes in the same way that extending objects in OO works.
Just because someone says (or writes) something does not make it true. You need some kind of logic to make a case. I would not use them in data modelling carelessly, but I think occasionally they can work nicely there. Mostly if you have some kind of linear pipeline process that builds up fields as processing works down the pipeline - it can be nice to model each stage as an extensible group of fields, with the final output made up of all the parts. The alternative of modelling each stage separately is that you might add a field to one stage but forget to add it to all the others further down the pipeline.
I used them once to define an extensible API with common base functions and multiple implementations - that was quite OO, but worked for the use case.
I think the advice should be “Extensible records are most useful for restricting arguments, not data-modeling”.
As newbie of elm, I like to read the new thread if it’s created .
To me, “restricting arguments” is highly related to “data-modeling”. It seems an elm application is pretty much just a big(flat) data(model) surrounded by a lot of logic(functions). So “restricting arguments” is an important way to deal with that data. By what I’ve learned about elm so far(which is limited BTW), it’s one of the key concepts one must grasp to build serious application.