But the compiler says I can’t add a z
field to a Point2 {}
record.
The compiler is right. point2
is a Point2 {}
. By the definition of Point2 a
you gave, this is a record of the shape
{ x : Int,
, y : Int
}
Now you’re trying to update it with { point2 | z = z }
. But there is no z
field! You can only update fields that already exists.
What happens in the verbose solution that you also gave is that you create a new record that has all fields, x
, y
, and z
. Now since you defined Point2 a
as an extended record, the output of makePoint3
will also be a Point2 a
, because it has x
and y
fields (and the additional z
).
Copying each field might feel repetitive, but there is an alternative, with some downsides to ergonomics:
type alias Point3 =
{ dd : Point2 {}, -- read dd as 2D :)
, z : Int
}
Now you’re not copying fields, but keeping the Point2 a
intact and composing it into a new record with additional information. Unfortunately, such a Point3 a
is not a Point2 a
, because it does not have the x
and y
fields (they are inside its dd
field). So for any function that needs a Point2 a
, you need to extract the dd
field and pass only that.
This approach is actually slightly nicer. Sure, you have to explicitly convert between 3D and 2D points, but it’s only a quick record field call and you see all conversion. I expect that there are a few subtle bugs that you will spare yourself if you have this explicit conversion. For this to work really well, I’d even suggest not making Point2 a
an extensible record. Just have a type alias Point2 = { x : Int, y : Int }
.
Extensible records are nice when you only care about a small set of fields of a record. The only good case I can imagine is a Model
record, since those necessarily will have a lot of fields. I think it is always better if you can operate on only one field at a time, but sometimes you will need to know about multiple things at once. And for those cases it can be nice to use extensible records to make working with a large struct easier. But I’d argue that it is preferrable to organize your record in a way that you do not need extensible records.
What was your reason to make Point2 a
an extensible record in the first place?