- if true then "abc" else 2.01;
stdIn:1.2-1.30 Error: types of if branches do not agree [tycon mismatch]
then branch: string
else branch: real
in expression:
if true then "abc" else 2.01
You would need to define a new data type to wrap them: - datatype string_or_real = r of real | s of string;
- if true then s "abc" else r 2.01;
val it = s "abc" : string_or_realwtf = A of string | B of float
And then you'd return A("abc") or B(2.01) and your function would be () -> wtf
I think the point you're making is that Standard ML has certain restrictions in its type system that are necessary to make type inference possible: there are no union types, no subtypes (more recent work has found a way to make subtyping compatible with full H-M), polymorphism has to be introduced explicitly with `let`, and there are no higher-kinded types. And in some cases you'd argue those restrictions might incur more code overhead compared to a language that loosens those restrictions at the cost of less reliable type inference (e.g. Scala).
That's sort-of-but-not-really true: in my experience union types and subtypes are always bad ideas and there are better alternatives for any use case where you would use them. You never actually want "String | Int", you want a type with meaningful semantics (like `Result<String, Int>`).
A good discussion of this can be found in https://dev.realworldocaml.org/variants.html#scrollNav-4
So in the original example, you can't unify "ssdf" and the int 33, but in the re-written version B("sfd") and A(33) have the same type and can be unified.
https://www.cs.cornell.edu/courses/cs3110/2011sp/Lectures/le...
import System.Random
x = do y <- randomIO :: IO Float -- note: this is before there are instances of randomIO for IO Int and other types
if y > 0.5 then
return (Left "abc")
else
return (Right 2.01)You would have to either:
- test for the type of that value in order to handle it properly,
- rely of the implicit cast rules of JS, which wouldn't be very useful here I suppose
As it has been said by others, in Ocaml (as well as in many other strongly typed languages) you can use a sum type to solve that problem.
Edit: In some languages, there is also the concept of "intersection types" which, if I understand correctly, let one also handle that sort of situation. The corresponding Wikipedia entry [1] gives a list of languages supporting that concept, and provides examples.
With "duck typing", you need to have an extra test for types which your code is not supposed to work with, whereas with sum types, the type checker will help you verify that it cannot happen.
That being said, there are situations which a strong type system will not be able to model.
Anyway, at the end of the day, what matters is that you (and your coworkers) feel comfortable with your code. I know by experience that it's not always the case.
To expand on another commenters example, suppose you have a sum type that models successful computation with a return value or a failure with an error message.
If you have tags / constructors, that's all easy. But if you just use naked unions, you can not write code that deals with error messages (or strings in general) in the success case.
randomReturner n =
if n < 0.5 then
123
else
"abc"
The compiler will give you the following info: The 2nd branch of this `if` does not match all the previous branches:
22| if n < 0.5 then
23| 123
24| else
25| "abc"
^^^^^
The 2nd branch is a string of type:
String
But all the previous branches result in:
number
Hint: All branches in an `if` must produce the same type of values. This way, no
matter which branch we take, the result is always a consistent shape. Read
<https://elm-lang.org/0.19.1/custom-types> to learn how to “mix” types.
Hint: Try using String.toInt to convert it to an integer?
You can try it out on Ellie here: https://ellie-app.com/9nXxfKSStG4a1Here's an example where the feature can save you:
```
let x = () => {
if (Math.random() > 0.5) {
return "abc"
} else {
return 4
}
}// ERROR: This condition will always return 'false' since the types '"abc" | 4' and '"sdf"' have no overlap.
if (x() === "sdf") {
do_work()
}```