What is the type annotation for a case expression with multiple record types?

65 views Asked by At

I have an update function in my Elm file.

It uses case expressions.

However, depending on the case, it is supposed to affect different parts of the model with different record types. Below is the code:

update : { a | desc : String, data : String } -> { c | selectedPhoto : String, currentSize : String } -> { c | selectedPhoto : String }
update msg db =
    case msg.desc of
    "ClickedPhoto"      -> { db | selectedPhoto = msg.data }
    "ClickedSurpriseMe" -> { db | selectedPhoto = "2.jpeg" }
    "ClickedSizeOption" -> { db | currentSize   = msg.data }
    _                   -> db

I don't have any issues if I don't add the annotations, but when I do, I get the below error message:

TYPE MISMATCH - Something is off with the 1st branch of this `case` expression:
56|     "ClickedPhoto"      -> { db | selectedPhoto = msg.data }
                               #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
The 1st branch is a record of type:
    { c | #currentSize# : String, selectedPhoto : String }
But the type annotation on `update` says it should be:
    { c | selectedPhoto : String }
#Hint#: Seems like a record field typo. Maybe #currentSize# should be #selectedPhoto#?
#Hint#: Can more type annotations be added? Type annotations always help me give
more specific messages, and I think they could help a lot in this case!

I don't want to change currentSize to selectedPhoto, as that would not get me the result I want. I also don't want to remove the type annotations.

How do I alter the type annotations to remove the error message?

1

There are 1 answers

0
glennsl On

You need to add currentSize to the return type as well:

update : { a | desc : String, data : String } -> { c | selectedPhoto : String, currentSize : String } -> { c | selectedPhoto : String, currentSize : String }

As I understand, the c type variable here does not refer to the record type as a whole, only the fields that are not explicitly listed. The return type is therefore missing currentSize since it's listed in the argument type and therefore not included in c.

To avoid the duplication you can also use a type alias:

type alias Db c = { c | selectedPhoto : String, currentSize : String }

update : { a | desc : String, data : String } -> Db c -> Db c