Like the OP, I was thinking about the Bool value being part of a returned tuple (so I was imagining a cardinality of 6 if not 7):
| Res | Bool | Color | Equiv |
| Ok | T | R | Ok (True, Red) |
| Ok | T | G | Ok (True, Green) |
| Ok | T | B | Ok (True, Blue) |
| Ok | F | R | Ok (False, Red) |
| Ok | F | G | Ok (False, Green) |
| Ok | F | B | Ok (False, Blue) |
| Err | _ | _ | Err String |
| | Total | 7 |
However, if Bool represents the Ok/Err state, I still fail to see how I should consider a cardinality of 5. In my mind, I see those branches.
| Res | Bool | Color | Equiv |
| Ok | T | R | Ok Red |
| Ok | T | G | Ok Green |
| Ok | T | B | Ok Blue |
| Err | F | _ | Err String |
| | Total | 4 |
I suppose this means that mean that:
Ok(Red | Green | Blue)
is to be considered as part of an element of the “set” of possible values?
I think I’m a little confused since I’m thinking in terms of truth tables, and that’s how I tend to think about multiplying the complexity of code branches and I relate this to if/else statements.
Could anyone clarify this for me? I’d like to understand how considering cardinality could help me write better code.
I understand how ORing a type means you have to add the elements of the set to obtain the cardinality, but I fail to visualise what’s going on here.
Thanks @Chadtech. Indeed I was confused about the Result type, thanks it makes perfect sense now.
@edgerunner, that’s how I understand it: 2 + ∞. Though in that case the infinity aspect isn’t much of a problem since we wouldn’t be doing anything complicated with the infinity of strings. I’d still be interested to hear when and if we still would want to reduce cardinality, maybe use an error dictionnary as a best practice?
Yes, the cardinality would be infinite for Result String Bool.
That said, depending on the use-case, you’re better off discarding the cardinality of String.
For instance, if you take the following example
| User String -- name of the user
| Admin String -- name of the admin
If you compute the cardinality, it is again infinite, because you can have an infinite number of different User with different names, and same for Admin. That said, in your data modeling, you will probably not care about the cardinality of name, so you can ignore it. Then you can count again and assess that the cardinality of possible users is 3, which is likely more interesting to your use-case.
What I understand from your answer is that the cardinality of a potentially infinite type depends on how I intend to branch on it. So maybe aList has a cardinality of 2, because it can be empty or non-empty, which makes a difference for me, or 3 if I also envision a different meaning for a single-element list. Does that make sense?
Yes. “The cardinality of something depends on how you will branch on it” is a good way to put it. But being explicit about how you are going to branch on it is probably a good move. If you want to have different behaviors for different sizes of your list you might want to do
type MyList a
| OneItem a
| TwoItems a a
| AtLeastThreeItems a a a (List a)
Here the cardinality of the type is infinite, but in a way, it’s also obvious that you’re going to have 4 different behaviours, thus in a way a cardinality of 4.
Let your data model guide how you will branch off explicitly instead of letting your case expressions determine that implicitly.
My guess is that the confusion resides in reading Result Bool Color in two different ways.
The first one, is the obvious one for most Elm users which is “The type Result with the error parameter set to Bool and the success parameter set to Color”.
and since Result is defined as type Result error success = Ok success | Err error this automatically means that the cardinality of the type is cardinality of Bool + cardinality of Color (5 in the example).
The second way to read that is to read it as the definition of a tag. type MyResult = Result Bool Color. In this case we have the tag Result with two parameters: Bool and Color. In this case, the cardinality of MyResult is cardinality of Bool times cardinality of Color (6). The cardinality of MyResult is the same as the cardinality of a tuple (Bool, Color)
Once one understands that the custom type in Elm are Tagged Unions, also called “sum types” they understand the cardinality of that custom type. The cardinality of a sum type will always be the sum of the cardinalities of its branches.
It is about pattern match exhaustion. If the type has non-infinite cardinality, you can explicitly match each case and have the compiler complain when you haven’t treated a case.
Pattern matching also allows you to match against a variable. This effectively reduces the cardinality of the match for that specific variable to 1. You can also reduce the cardinality to a larger number by matching to explicit values before you match for the variable.