Reputation: 368
I'm trying to extract some information from some records using extensible records. If I create a function taking the extensible record type and returning a string and use that within a case statement then there are no issues (namedToString
, in the below example). However, if I attempt to use a function passed as a parameter (stringFromNamed
) I will get an error complaining:
This business value is a:
Business
But stringFromNamed needs the 1st argument to be:
Named a -> String (edited)
from the example code below:
type alias Named a =
{ a | name : String }
type alias Person =
Named { address : String }
type alias Business =
Named { employeeCount : Int }
type Change
= PersonUpdate Person
| BusinessUpdate Business
namedToString : Named a -> String
namedToString changeFields =
changeFields.name
changeToString : (Named a -> String) -> Change -> String
changeToString stringFromNamed change =
case change of
PersonUpdate person ->
-- This works
namedToString person
BusinessUpdate business ->
-- This will cause the error
stringFromNamed business
This example includes just the required code but a more complete example can be found at https://ellie-app.com/77nCPLh55j3a1
What is causing the issue and how can I achieve my goal of passing in a function which will extract some information from an extensible record?
Upvotes: 3
Views: 271
Reputation: 368
I discovered this issue in the Elm compiler repo: https://github.com/elm/compiler/issues/1959
This appears to be a bug in the compiler which can be worked around if you just want to pass an external record of one type to a function parameter. You can do this by removing the type signature of the higher order function, changeToString
in the example above.
Unfortunately, if we wanted to perform the same action from the function parameter, e.g. stringFromNamed
, on every case the error will return so another workaround must be found.
The workaround I'm using is to create a new record type which includes exactly the fields from the extensible record and then creating an instance of this from the fields of the other records adhering to the extensible record type. Not a big problem with only a couple of cases and an extensible record with only one field but this doesn't scale especially well. Example below:
type alias Named a =
{ a | name : String }
type alias OnlyNamed =
Named {}
type alias Person =
Named { address : String }
type alias Business =
Named { employeeCount : Int }
type Change
= PersonUpdate Person
| BusinessUpdate Business
type Msg
= UpdateCurrentChange Change
namedToString : Named a -> String
namedToString changeFields =
changeFields.name
changeToString : (OnlyNamed -> String) -> Change -> String
changeToString stringFromNamed change =
case change of
PersonUpdate { name } ->
stringFromNamed { name = name }
BusinessUpdate { name } ->
stringFromNamed { name = name }
Upvotes: 4
Reputation: 155935
I think the issue is that the function you're passing in takes Named a
, and Elm doesn't know what the a
refers to, so it ends up deciding it's a type mismatch. If you change the type annotation to (Named Business -> String) -> Change -> String
it will compile and work.
Upvotes: 1