Elm has polymorphism, but we usually cannot create polymorphic values. An example:
foldr : (a -> b -> b) -> b -> List a -> b
To call this, I need a list of something:
foldr (\w acc -> w ++ " " ++ acc) "" ["words", "to", "join", "with", "spaces"]
Its polymorphic, but I needed to give it some values to do anything useful, and that means that the program ended up fully typed at runtime. Ok, I don’t have to give it concrete values immediately, the polymorphism could continue up the call stack as many times as we like, but ultimately somewhere has to provide real values.
Also worth noting that the empty list can be polymorphic (List a); in some sense all empty lists are equivalent despite their possible differences in type.
There is a way in Elm that I can create a non-empty value that has a variable type:
num : number
num = 1
Elm does not require that Float has a decimal point in it, so when I write an integer it can be Int or Float until the type checker constrains it one way or the other.
But will that always happen ?
Elm Programs can have arbitrary type variables. You might know this from having done some fancy thing with Programs in the past. It is possible for unconstrained type variables to bubble all the way up to the Program level, and they can then escape the Program entirely there:
main : Program () (Model number) Msg
So now I can have:
type alias Model number =
{ count : number }
And I can really instantiate that with a variably typed value, see num above.
But can I ever instantiate it with a value that is not actually an Int?
untypedModel : Bool -> Model number
untypedModel isInt =
if isInt then
{ count = 1 }
else
{ count = 1.0 }
But that won’t pass typechecking, since both branches must have the same type, they unify to Model Float and that is not the same as Model number:
Something is off with the 2nd branch of this `if` expression:
23| { count = 1.0 }
^^^^^^^^^^^^^^^
The 2nd branch is a record of type:
{ count : Float }
But the type annotation on `untypedModel` says it should be:
Model number
I also tried passing in a number as a flag:
main : Program number (Model number) Msg
And the type checker also rejected that:
Your `main` program wants an unspecified type from JavaScript.
55| main =
^^^^
But type variables like `number` cannot be given as flags. I need to know
exactly what type of data I am getting, so I can guarantee that unexpected data
cannot sneak in and crash the Elm program.
If we took an Elm program and type checked it, then replaced all occurences of values with number type that remain with Int, I think the result would be an Elm program that still type checked and had the exact same runtime behaviour as the original.
I don’t believe there are any other ways in Elm that we can create instances of values that are untyped.
Therefore, even though Elm programs can be polymorphic, they can never be untyped.
I just wanted to check that my reasoning here is sound, and that there is nothing I have missed ? no sneaky workarounds ? No devious type shenanigans that would falsify that assertion ?