Ki Yung Ahn
kyagrd@gmail.com
( this slide is available online at http://slides.com/kyagrd/TIPERdundee )
Andrea Vezzosi
vezzosi (AT) chalmers (DOT) se
http://www.cse.chalmers.se/~vezzosi/
however, in the real world ...
Why on earth there are so many
dynamically-typed untyped languages
and still more piling up every new year?
Langugae Construction tool-stack avialble today
Language System Front-end Construction tool-stack
?- type([], lam(x,var(x)), A->A).
true .
?- type([], lam(x,var(x)), T).
T = (_G123->_G123) .
?- type([], E, A->A).
E = lam(_G234,var(_G234)) .
A functional specification would need several different functions for each functionality, which will inevitably cause duplication in the specification.
type(C,var(X), T1) :- first(X:T,C), instantiate(T,T1).
type(C,lam(X,E), A -> B) :- type([X:mono(A)|C],E,B).
type(C,X $ Y, B ) :- type(C,X,A -> B), type(C,Y,A).
type(C,let(X=E0,E1), T ) :- type(C,E0,A),
type([X:poly(C,A)|C],E1,T).
first(K:V,[K1:V1|Xs]) :- K = K1, V=V1.
first(K:V,[K1:V1|Xs]) :- K\==K1, first(K:V, Xs).
instantiate(poly(C,T),T1) :- copy_term(t(C,T),t(C,T1)).
instantiate(mono(T),T).
Haskell supports parametric polymorphism over type constructors as well as types, historically motivated to support the monad class, which classifies over type constructors, but useful for defining generic form of datatypes even when type classes aren't involved.
-- Tree :: (* -> *) -> * -> *
data Tree c a
= Leaf a -- Leaf :: Tree c a
| Node (c (Tree c a)) -- Node :: (c(Tree c a)) -> Tree c a
type BinTree a = Tree Pair a -- two children on each node
type Pair t = (t,t)
type RoseTree a = Tree [] a -- varying number of children
kind(KC,var(Z),K1) :- first(Z:K,KC), instantiate(K,K1).
kind(KC,F $ G, K2) :- kind(KC,F,K1 -> K2), kind(KC,G,K1).
kind(KC,A -> B,o) :- kind(KC,A,o), kind(KC,B,o).
type(KC,C,var(X), T1) :- first(X:T,C), inst_type(KC,T,T1).
type(KC,C,lam(X,E), A->B) :- type(KC,[X:mono(A)|C],E,B),
kind(KC,A->B,o).
type(KC,C,X $ Y, B) :- type(KC,C,X,A->B), type(KC,C,Y,A).
type(KC,C,let(X=E0,E1),T) :- type(KC,C,E0,A),
type(KC,[X:poly(C,A)|C],E1,T).
% first and instantiate are same as before in HM
% inst_type does what instantiate does but adds more
% kind assertions for freshly instantiated variables
kind(KC,var(Z),K) :- first(Z:K,KC).
kind(KC,F $ G,K2) :- kind(KC,F,K1 -> K2), kind(KC,G,K1).
kind(KC,A -> B,o) :- kind(KC,A,o), kind(KC,B,o).
type(KC,C,var(X), T) :- first(X:T,C).
type(KC,C,lam(X,E), A->B) :- type(KC,[X:A|C],E,B),
kind(KC,A->B,o).
type(KC,C,X $ Y, B) :- type(KC,C,X,A->B), type(KC,C,Y,A).
?- type([], [], lam(x,var(x)), A->A).
ERROR: Out of local stack
Exception: (1,456,711) kind([], _G8740235, (_G8740238->
_G8740232->_G8740226->_G8740220-> ... -> ...)) ? abort
% Execution Aborted
A twist to make type & kind inference work in Prolog
using Definite Clause Grammar (DCG) rues as
a neat syntax for a writer monad that collects
kind assertions as a side-output to be called upon later.
kind(KC,var(Z),K1) :- first(Z:K,KC), instantiate(K,K1).
kind(KC,F $ G, K2) :- kind(KC,F,K1 -> K2), kind(KC,G,K1).
kind(KC,A -> B,o) :- kind(KC,A,o), kind(KC,B,o).
type(KC,C,var(X), T1) --> first(X:T,C), inst_type(KC,T,T1).
type(KC,C,lam(X,E), A->B) --> type(KC,[X:mono(A)|C],E,B),
[ kind(KC,A->B,o) ]. % delayed goal
type(KC,C,X $ Y, B) --> type(KC,C,X,A->B), type(KC,C,Y,A).
type(KC,C,let(X=E0,E1),T) --> type(KC,C,E0,A),
type(KC,[X:poly(C,A)|C],E1,T).
?- phrase(type([],[],lam(x,var(x)), Gs).
T = (_G2662->_G2662),
Gs = [kind([], (_G2662->_G2662), o)] .
Freeze the Prolog variables in types into
variables for kind inference and also
extend the kinding context with them
?- kind([t1:mono(K1)], (var(t1)->var(t1)), o).
K1 = o
For further details (including pattern matching, Mendler-style recursion)
see
1st stage of
type inference
2nd stage of
kind inference
pre-processing for 2nd stage
Type Inference Prototyping Engine
from Relational specifications
of type systems