# I tend to use `again` as the name for recursive calls when using `fix`:
factorial := (fix .[ again : [ -> [ Int -> Int ]] .  .[ n : Int . match (zero? n) { +[True]+ => one  +[False]+ => (times n ((again) (minus n one))) } ]. ].)

# Recursive types need `mu_type`:
IntList t= mu_type IntList . { +[Nil]+ +[Cons Int IntList]+ }

# The unfolded form is useful because that's what the constructors create:
IntListUF t= { +[Nil]+ +[Cons Int IntList]+ }

# Constructing an enum requires a type annotation, as does `fold`.
empty_ilist := fold +[Nil]+ : IntListUF : IntList
ilist_3 := fold +[Cons three empty_ilist]+ : IntListUF : IntList
ilist_23 := fold +[Cons two ilist_3]+ : IntListUF : IntList
ilist_123 := fold +[Cons one ilist_23]+ : IntListUF : IntList


sum_int_list := (fix .[again : [-> [IntList -> Int]] . .[ lst : IntList . match unfold lst { +[Nil]+ => zero +[Cons hd tl]+ => (plus hd ((again) tl))} ]. ]. )

# Parametric recursive types:
# Note: There's no type relationship between `IntList` and `List<Int>`. Use the latter!
List t= forall T . mu_type List . { +[Nil]+ +[Cons T List<T> ]+ }
ListUF t= forall T . { +[Nil]+ +[Cons T List<T> ]+ }

# Accepting parametrically-typed arguments:
list_len := forall T . (fix .[again : [-> [List<T> -> Int]] . .[ lst : List<T> . match unfold lst { +[Nil]+ => zero +[Cons hd tl]+ => (plus one ((again) tl))} ]. ].)

empty_int_list := fold +[Nil]+ : ListUF<Int> : List<Int>

# In order to prevent having to write *two* type annotations every cons, let's define a helper function:
cons := forall T . .[ car : T  cdr : List<T>  .  fold +[Cons car cdr]+ : ListUF<T> : List<T> ].

list_3 := (cons three empty_int_list)
list_23 := (cons two list_3)
list_123 := (cons one list_23)

# Now try `(list_len list_123)`!
# Remember to save useful things to the prelude!
