I have type Model
, which describes two possible states of generic type alias ModelFields
. And I want to extract generic ModelFields
record from an instance of the Model
type.
type Model endValue stats
= ShowEndValues (ModelFields Organism endValue)
| ShowStatistics (ModelFields Rank stats)
type alias ModelFields object results =
{ results : List results
, objects : List object
, cellValue : results -> String
, location : Maybe (Rank, Rank)
}
getModelFields : Model endValue stats -> ModelFields object results
getModelFields model =
case model of
ShowEndValues modelFields ->
modelFields
ShowStatistics modelFields ->
modelFields
But Elm does not allow it saying for each case expression that
TYPE MISMATCH - Something is off with the 1st branch of this `case` expression:
55| modelFields
#^^^^^^^^^^^#
This `modelFields` value is a:
ModelFields #Organism# #endValue#
But the type annotation on `getModelFields` says it should be:
ModelFields #object# #results#
#Hint#: Your type annotation uses type variable `object` which means ANY type of
value can flow through, but your code is saying it specifically wants a
`Organism` value. Maybe change your type annotation to be more specific? Maybe
change the code to be more general?
So my question is: how do I get ModelFields
from Model
? Or am I doing something fundamentally flawed?
UPD. Details of what I'm trying to model.
I have objects of type Organism
. They are grouped into Rank
s. My server does some pairwise analyses of Organism
s, i.g. calculates Similarity
and Distance
between two Organism
s. I'd like to display the results of these analyses in two tables on different pages, one page for Similarity
analysis, and one page for Distance
analysis. This means the table should be reusable to accept analysis results of any form.
On the other hand, there is one common pattern for these tables. They can be in two states:
endValue
in Model
) between Organism
s, so the table's rows and columns represent Organism
s.stats
in Model
) between groups (Rank
s) of Organism
s, in this case the table's rows and columns represent Rank
s.Which Rank
s or Organism
s are shown depends on location
field of ModelFields
. User can click a button and I'd like to change the location
in my update
function. Potentially this navigation change can switch between ShowEndValues
and ShowStatistics
states. That's why I'm trying to unwrap ModelFields
from Model
constructors. I'm attaching a simple illustration in hope it helps to clarify.
This is indeed not possible.
The model
value in the getModelFields
function will have type Model endValue stats
. This means that it will either be ShowEndValues
variant, containing a value of type ModelFields Organism endValue
, or the ShowStatistics
variant containing a value typed ModelFields Rank stats
. The each branch of the case
expression unpacks one variant and outputs the modelFields
value.
Now, let’s try to determine the type of the case
expression. We look at the types returned by each branch and try to find a type that encompasses both types (unify them). On a first sight, it looks promising: both values are in the form ModelFields a b
so we will recursively try to unify each of the child types. Here we come across a problem – a
of the first type has type Organism
whereas the second one is Rank
. There is not type that is both Organism
and Rank
so the compilation fails.
Note: As you can see from the error message, Elm actually tries to unify the types of the branches with the function result type. (I described the other direction because I think it is clearer to understand.) The direction followed by Elm also fails because it will recurse and try to unify a concrete Organism
type with a generic object
type, similarly to how we tried to unify two concrete types.