Types
Simple types
\[ \gdef\code#1{\mathtt{#1}} \gdef\typeof#1{\left<{#1}\right>} \gdef\par#1{\left({#1}\right)} \]
Take the exemplar line
\[ \code{fact}\ \code{n}\ \code{return}\ \code{:}\ \code{isZero}\ \code{n}\ \code{base}\ \code{recurse} \]
Denote the type of a parameter \(x\) as \(\typeof{x}\) and the type of a closure as \(\par{a_1, a_2, \dots}\) where \(a_1\) is the type of the first parameter, etc. Then the above line implies two type equations:
\[ \begin{aligned} \typeof{\code{fact}} &= \par{ \typeof{\code{n}}, \typeof{\code{return}}} \\ \typeof{\code{isZero}} &= \par{ \typeof{\code{n}}, \typeof{\code{base}}, \typeof{\code{recurse}} } \end{aligned} \]
From this it follows that there are two type constraints for every declaration.
Example
Let’s take the factorial function in Oluś Zero:
fact n return: isZero n base recurse
base: return 1
recurse: sub n 1 step1
step1 m: fact m step2
step2 f: mul n f return
It has five declarations, so we obtain ten equations:
\[ \begin{aligned} \typeof{\code{fact}} &= \par{ \typeof{\code{n}}, \typeof{\code{return}}} & \typeof{\code{isZero}} &= \par{ \typeof{\code{n}}, \typeof{\code{base}}, \typeof{\code{recurse}} }\\ \typeof{\code{base}} &= \par{}& \typeof{\code{return}} &= \par{ \code{Int} }\\ \typeof{\code{recurse}} &= \par{}& \typeof{\code{sub}} &= \par{ \typeof{\code{n}}, \code{Int}, \typeof{\code{step1}} }\\ \typeof{\code{step1}} &= \par{ \typeof{\code{m}} }& \typeof{\code{fact}} &= \par{ \typeof{\code{m}}, \typeof{\code{step2}} }\\ \typeof{\code{step2}} &= \par{ \typeof{\code{f}} }& \typeof{\code{mul}} &= \par{ \typeof{\code{n}}, \typeof{\code{f}}, \typeof{\code{return}}, }\\ \end{aligned} \]
This can be simplified by substitution:
\[ \begin{aligned} \typeof{\code{fact}} &= \par{ \typeof{\code{n}}, \par{\code{Int}} }& \typeof{\code{isZero}} &= \par{ \typeof{\code{n}}, \par{}, \par{} }\\ \typeof{\code{base}} &= \par{}& \typeof{\code{return}} &= \par{ \code{Int} }\\ \typeof{\code{recurse}} &= \par{}& \typeof{\code{sub}} &= \par{ \typeof{\code{n}}, \code{Int}, \par{\typeof{\code{m}}} }\\ \typeof{\code{step1}} &= \par{ \typeof{\code{m}} }& \typeof{\code{fact}} &= \par{ \typeof{\code{m}}, \par{\typeof{\code{f}}} }\\ \typeof{\code{step2}} &= \par{ \typeof{\code{f}} }& \typeof{\code{mul}} &= \par{ \typeof{\code{n}}, \typeof{\code{f}}, \par{\code{Int}} }\\ \end{aligned} \]
Observe that \(\typeof{\code{fact}}\) appears twice on the left-hand side. Equating the right-hand sides gives \(\typeof{\code{n}} = \typeof{\code{m}}\) and \(\typeof{\code{f}} = \code{Int}\).
\[ \begin{aligned} \typeof{\code{fact}} &= \par{ \typeof{\code{m}}, \par{\code{Int}} }& \typeof{\code{isZero}} &= \par{ \typeof{\code{m}}, \par{}, \par{} }\\ \typeof{\code{base}} &= \par{}& \typeof{\code{return}} &= \par{ \code{Int} }\\ \typeof{\code{recurse}} &= \par{}& \typeof{\code{sub}} &= \par{ \typeof{\code{m}}, \code{Int}, \par{\typeof{\code{m}}} }\\ \typeof{\code{step1}} &= \par{ \typeof{\code{m}} }& \typeof{\code{fact}} &= \par{ \typeof{\code{m}}, \code{Int} }\\ \typeof{\code{step2}} &= \par{ \code{Int} }& \typeof{\code{mul}} &= \par{ \typeof{\code{m}}, \code{Int}, \par{\code{Int}} }\\ \typeof{\code{n}} &= \typeof{\code{m}} & \typeof{\code{f}} &= \code{Int} \end{aligned} \]
We have only one variable left. But we did not yet include the types of the builtin functions, which are:
\[ \begin{aligned} \typeof{\code{isZero}} &= \par{ \code{Int}, \par{}, \par{} }\\ \typeof{\code{sub}} &= \par{ \code{Int}, \code{Int}, \par{\code{Int}} }\\ \typeof{\code{mul}} &= \par{ \code{Int}, \code{Int}, \par{\code{Int}} } \end{aligned} \]
With these included, it becomes clear that the types of all procedures and parameters are:
\[ \begin{aligned} \typeof{\code{fact}} &= \par{ \code{Int}, \par{\code{Int}} }& \typeof{\code{return}} &= \par{ \code{Int} }\\ \typeof{\code{base}} &= \par{} & \typeof{\code{recurse}} &= \par{} \\ \typeof{\code{step1}} &= \par{ \code{Int} } & \typeof{\code{step2}} &= \par{ \code{Int} } \\ \typeof{\code{n}} &= \code{Int} & \typeof{\code{m}} &= \code{Int} \\ \typeof{\code{f}} &= \code{Int} \end{aligned} \]
Our factorial function has now been satisfactory type-checked.
Limitations
See also
https://eli.thegreenplace.net/2018/type-inference/
https://eli.thegreenplace.net/2018/unification/
http://okmij.org/ftp/ML/generalization.html