I have this code
let my_fun: [`X | `Y | `Z] -> [`Z | `Y] = function
| `X -> `Y
| x -> x
It won't compile with message
11 | | x -> x
^
Error: This expression has type [ `X | `Y | `Z ]
but an expression was expected of type [ `Y | `Z ]
The second variant type does not allow tag(s) `X
Why can't the complire infer the type [
if it is clear that `X can be never returned from the function?Z |
Y]
Why can't the compiler infer the type [Z |Y] if it is clear that `X can be never returned from the function?
A simpler example is:
if false then 0 else "hello"
The compiler has to reject it because typing in ML languages requires that both branches have the same type 1, and types int
and string
cannot be unified; this is true even though you could say that there is no chance the expression could ever evaluate to 0 (keep in mind however that formally speaking, it makes no sense to say we evaluate an expression that is not part of the language as defined).
In a match
expression, the types of the left part of all clauses must unify to the type of the expression being matched. So x
in the last clause has the same type as the implicit argument in function
, which is the input type declared in the signature. This is true whether or not `X
is a valid value in that context.
You need to enumerate all the cases that are valid; if you need this often in your code then you'd better write a dedicated function for that:
let f : ([> `Y | `Z ] -> [`Y | `Z ] option) = function
| (`Y|`Z) as u -> (Some u)
| _ -> None;
For example:
# f `O;;
- : [ `Y | `Z ] option = None
# f `Y;;
- : [ `Y | `Z ] option = Some `Y
See also Set-Theoretic Types for Polymorphic Variants (pdf)
[1] For comparison, in Common Lisp, which is dynamically typed, the equivalent expression is valid; a static analysis as done by SBCL's compiler can infer its type, i.e. a string of length 5:
> (describe (lambda () (if nil 0 "hello")))
....
Derived type: (FUNCTION NIL
(VALUES (SIMPLE-ARRAY CHARACTER (5)) &OPTIONAL))