hello
so, the type of []
is List *
means *
denotes bottom type
but also
isEmpty : List * -> Bool
The * is a wildcard type; a type that's compatible with any other type. List * is compatible with any type of List like List Str, List Bool, and so on.
so that means * is also the top type?
let's see.
{}
for this type[]
the *
is a forall-quantified type variable. The signature is really isEmpty : forall a. List a -> Bool
within the implementation of isEmpty
, that means you have no concrete information about what type of elements are in the list (and to implement that function, you don't care)
then the type of []
should be List []
, not List *
, no?
but certainly, we cannot construct (within the body) a function forall b. b -> a
. We don't have any value of type a
that we can make out of thin air
likewise, we cannot create forall b. a -> b
, because we can also not create a value of any type b
out of thin air
hence a forall-quantified variable is neither top nor bottom
well, that is valid, but not very helpful in practice
say I want to do List.append [] 42
then if the empty list had type List []
, we'd get a type error here
because 42
does not have type []
so in type inference, the type of the list is left variable
however, when we go to monomorphize, we have to pick an actual concrete type in that position
and there our choice is free. Any type will do, that is what the signature says!
in practice picking {}
is usually the most convenient choice
can it work with List.append 42 []
?
can what work with List.append [] 42
(in roc, the data structure is usually the first argument)?
ah I see, ok
I guess, that the *
has different meaning on the left vs. on the right, is what makes it confusing here.
if we are to e.g. make *
to Anything
on the left, and Nothing
on the right, it would be much clearer.
the confusion is even in the tutorial
so we might think the type of List.reverse would be reverse : List * -> List *. However, remember that we also saw that the type of the empty list is List *? List * -> List * is actually the type of a function that always returns empty lists! That's not what we want.
e.g. imagine if it's List Anything -> List Nothing
, much clearer
Probably that section in the tutorial should be reworded. List * -> List * is a function that accepts lists of any type (easy) and produces a list of any type you want (impossible to satisfy, except by producing the empty list).
Roc doesn’t have a hierarchy of types, so there aren’t really top/bottom types - maybe we can find a way to make thinking about them in that way more meaningful though.
if it helps, List * -> List *
is syntax sugar for List a -> List b
because in the general case, *
is syntax sugar for "a type variable whose name appears nowhere else in this type annotation"
Richard Feldman said:
because in the general case,
*
is syntax sugar for "a type variable whose name appears nowhere else in this type annotation"
That'd be useful wording to have in tutorials
Thanks for pointing that out @Kevin Gillette I'll make an issue and add in my next go at the tutorial.
Last updated: Jul 06 2025 at 12:14 UTC