Relational Specifications of Polymorphic Type Systems using Prolog

Ki Yung Ahn

Korea University, Sejong City, Korea

http://kyagrd.github.io/

13th International Symposium on Functional and Logic Programming (FLOPS 2016), Kochi, Japan
2016-03-05 Friday (金) Session 4. Logic Programming for type systems

best viewed on Chrome, available online at https://slides.com/kyagrd/tiper-flops2016​

Andrea Vezzosi

Chalmers University of Technology, Sweden

http://www.cse.chalmers.se/~sizan/

Type
Inference
Prototyping
Engines from
Relational Specifications of
                               Type Systems

\Gamma
Γ\Gamma
\vdash
\vdash
:
::
\forall
\forall
\lambda
λ\lambda

best viewed on Chrome, available online at https://slides.com/kyagrd/tiper-flops2016

Glories of Static Types in the Real World

Safety-Critical Software

e.g.,

  • ISO 2662, Road Vehicles - Functional Safety
    • ISO 2662-6: Table 1 (1c) Enforcement of strong typing
  • EN 50128, Railway Industry Specific - Software safety review
    • Table A.4 (8) - Strongly Typed Programming Language

9 Most In-Demand Prog. Lang. of 2016

Rank
Change Language Share Trend
1 Java 24.2 % +0.3 %
2 Python 11.9 % +1.2 %
3 PHP 10.7 % -0.8 %
4 C# 8.9 % +0.1 %
5 C++ 7.6 % -0.5 %
6 C 7.5 % +0.1 %
7 Javascript 7.3 % +0.3 %
8 Objective-C 5.0 % -0.9 %
9 Swift 3.0 % +0.4 %
10 R 2.9 % +0.3 %
11 Matlab 2.8 % -0.3 %
12 Ruby 2.3 % -0.2 %
13 Visual Basic 1.8 % -0.4 %
14 VBA 1.5 % +0.1 %
15 Perl 1.1 % -0.1 %
16 Scala 0.9 % +0.2 %
17 lua 0.5 % +0.0 %
© Pierre Carbonnelle, 2015

PYPL PopularitY of Prog. Lang. Index

Worldwide, Mar 2016 compared to a year ago:

Type Systems being re-invented

Lack of automated tools for building type systems

High development cost to adopt innovations from type theory & PL research

Inflexible and/or Verbose static type systems in mainstream langauges

Static Types
considered harmful, let's
use Dynamic Languages!

Oops, too painful to refactor/API-update
 without static types

Okay, let's add static types
Flow type checker for JavaScript, TypeScript, mypy, Typed Clojure, Typed Lua,  ... ...,  and all those fancy projects on gradual typing

trending in the real world

Type Systems need to be
Flexible & Succinct

(highly Polymorphic)

Problem

(good Type Inference)

difficult to implement good type inference

for highly polymorphic type systems

Solution?

automatically generate implementations
from type system specifications

What is TIPER?

Lex/Yacc : Parsers

TIPER : Type Systems

the missing automation tool
in langauge frontend construction

Outline

  • Introduction

  • Relational Specifications

  • Logic Programming

  • Preliminary Results using Prolog

  • TIPER project - Progress & Plans

  • Related Work

Type System on paper

Type System implementation (algorithmic/functional description)

\frac{x:A\;\in\;\Gamma}{ \Gamma\;\vdash\;x\;:\;A}
x:AΓΓx:A\frac{x:A\;\in\;\Gamma}{ \Gamma\;\vdash\;x\;:\;A}
\frac{\Gamma,\,x:A\;\vdash\;e\,:\,B}{ \Gamma\;\vdash\;\lambda x.e\;:\;A\to B}
Γ,x:Ae:BΓλx.e:AB\frac{\Gamma,\,x:A\;\vdash\;e\,:\,B}{ \Gamma\;\vdash\;\lambda x.e\;:\;A\to B}
\frac{ \Gamma\;\vdash\;e_1\,:\,A\to B \qquad \Gamma\;\vdash\;e_2\,:\,A }{ \Gamma\;\vdash\;e_1\;e_2\;:\;B }
Γe1:ABΓe2:AΓe1e2:B\frac{ \Gamma\;\vdash\;e_1\,:\,A\to B \qquad \Gamma\;\vdash\;e_2\,:\,A }{ \Gamma\;\vdash\;e_1\;e_2\;:\;B }
tyChk :: (Ctx, Exp, Type) -> Bool
tyChk(g, Var x, a)         = (x,a) `elem` g 
tyChk(g, Abs x e, Arr a b) = tyChk((x,a):g, e, b)
tyChk(g, App e1 e2, b)     = case tyInf(g,e1) of { Arr a b -> tyChk(g,e2,a)
                                                 ; _       -> False } 
tyInf :: (Ctx, Exp) -> Maybe Type
tyInf(g, Var x)     = lookup x gamma
tyInf(g, Abs x e)   = ... -- actually need some more magic here
tyInf(g, App e1 e2) = case tyInf(gamma,e1) of { Arr a b
                                                  | tyChk(g,e2,a) -> Just b
                                              ; _                 -> Nothing }

(Var)

(Abs)

(App)

Type System on paper

Problems with Algorithmic/Functional description

\frac{x:A\;\in\;\Gamma}{ \Gamma\;\vdash\;x\;:\;A}
x:AΓΓx:A\frac{x:A\;\in\;\Gamma}{ \Gamma\;\vdash\;x\;:\;A}
\frac{\Gamma,\,x:A\;\vdash\;e\,:\,B}{ \Gamma\;\vdash\;\lambda x.e\;:\;A\to B}
Γ,x:Ae:BΓλx.e:AB\frac{\Gamma,\,x:A\;\vdash\;e\,:\,B}{ \Gamma\;\vdash\;\lambda x.e\;:\;A\to B}
\frac{ \Gamma\;\vdash\;e_1\,:\,A\to B \qquad \Gamma\;\vdash\;e_2\,:\,A }{ \Gamma\;\vdash\;e_1\;e_2\;:\;B }
Γe1:ABΓe2:AΓe1e2:B\frac{ \Gamma\;\vdash\;e_1\,:\,A\to B \qquad \Gamma\;\vdash\;e_2\,:\,A }{ \Gamma\;\vdash\;e_1\;e_2\;:\;B }

(Var)

(Abs)

(App)

  • Gap from the original specification on paper
  • Duplication inevitable (type check and type infer )

Type System on paper

Relational Specification

\frac{x:A\;\in\;\Gamma}{ \Gamma\;\vdash\;x\;:\;A}
x:AΓΓx:A\frac{x:A\;\in\;\Gamma}{ \Gamma\;\vdash\;x\;:\;A}
\frac{\Gamma,\,x:A\;\vdash\;e\,:\,B}{ \Gamma\;\vdash\;\lambda x.e\;:\;A\to B}
Γ,x:Ae:BΓλx.e:AB\frac{\Gamma,\,x:A\;\vdash\;e\,:\,B}{ \Gamma\;\vdash\;\lambda x.e\;:\;A\to B}
\frac{ \Gamma\;\vdash\;e_1\,:\,A\to B \qquad \Gamma\;\vdash\;e_2\,:\,A }{ \Gamma\;\vdash\;e_1\;e_2\;:\;B }
Γe1:ABΓe2:AΓe1e2:B\frac{ \Gamma\;\vdash\;e_1\,:\,A\to B \qquad \Gamma\;\vdash\;e_2\,:\,A }{ \Gamma\;\vdash\;e_1\;e_2\;:\;B }

(Var)

(Abs)

(App)

  • Reduces the gap from the description on paper
  • Single Source of Truth (Don't Repeat Yourself )

Relational Spec. using Prolog

:- set_prolog_flag(occurs_check,true).
:- op(500,yfx,$).

type(C,var(X),       T) :- first(X:T,C).
type(C,lam(X,E),A -> B) :- type([X:A|C], E,  B).
type(C,E1 $ E2,      B) :- type(C,E1,A->B),
                           type(C,E2,A).

first(K:V,[K1:V1|Xs]) :- K = K1, V = V1.
first(K:V,[K1:V1|Xs]) :- K\==K1, first(K:V, Xs).
\frac{x:A\;\in\;C}{ C\;\vdash\;x\;:\;A}
x:ACCx:A\frac{x:A\;\in\;C}{ C\;\vdash\;x\;:\;A}
\frac{C,\,x:A\;\vdash\;e\,:\,B}{ C\;\vdash\;\lambda x.e\;:\;A\to B}
C,x:Ae:BCλx.e:AB\frac{C,\,x:A\;\vdash\;e\,:\,B}{ C\;\vdash\;\lambda x.e\;:\;A\to B}
\frac{ C\;\vdash\;e_1\,:\,A\to B \quad C\;\vdash\;e_2\,:\,A }{ C\;\vdash\;e_1\;e_2\;:\;B }
Ce1:ABCe2:ACe1e2:B\frac{ C\;\vdash\;e_1\,:\,A\to B \quad C\;\vdash\;e_2\,:\,A }{ C\;\vdash\;e_1\;e_2\;:\;B }

Outline

  • Introduction

  • Relational Specification

  • Logic Programming

  • Preliminary Results using Prolog

  • TIPER project - Progress & Plans

  • Related Work

Logic Programming

  • Executable Relational Specification

  • Semantics exist for LP languages

    • incrementally builds up a substitution

    • can inspect intermediate results (not a black box) 

  • Unification is a primitive operation in LP

    • basic building block for type inference algorithms 

Why not something else?

  • Possible choices for Relational Spec.

    • Inductive Definitions in Interactive Theorem Provers

    • Logic Programming

    • Constraint Solvers / Automated Theorem Provers

  • Inductive Defs in ITPs are good for proofs

    • Not usually best suited for execution
  • Solvers and ATPs are generally black box

    • No semantics, hard to inspect intermediate result

    • Solvers are usually difficult to extend

    • When to solve constraints to detect inconsistency is not always a trivial decision to make

Outline

  • Introduction

  • Relational Specification

  • Logic Programming

  • Preliminary Results using Prolog

  • TIPER project - Progress & Plans

  • Related Work

Simply-Typed Lambda Calculus

:- set_prolog_flag(occurs_check,true).
:- op(500,yfx,$).

type(C,var(X),       T) :- first(X:T,C).
type(C,lam(X,E),A -> B) :- type([X:A|C], E,  B).
type(C,E1 $ E2,      B) :- type(C,E1,A->B),
                           type(C,E2,A).

first(K:V,[K1:V1|Xs]) :- K = K1, V = V1.
first(K:V,[K1:V1|Xs]) :- K\==K1, first(K:V, Xs).
\frac{x:A\;\in\;C}{ C\;\vdash\;x\;:\;A}
x:ACCx:A\frac{x:A\;\in\;C}{ C\;\vdash\;x\;:\;A}
\frac{C,\,x:A\;\vdash\;e\,:\,B}{ C\;\vdash\;\lambda x.e\;:\;A\to B}
C,x:Ae:BCλx.e:AB\frac{C,\,x:A\;\vdash\;e\,:\,B}{ C\;\vdash\;\lambda x.e\;:\;A\to B}
\frac{ C\;\vdash\;e_1\,:\,A\to B \quad C\;\vdash\;e_2\,:\,A }{ C\;\vdash\;e_1\;e_2\;:\;B }
Ce1:ABCe2:ACe1e2:B\frac{ C\;\vdash\;e_1\,:\,A\to B \quad C\;\vdash\;e_2\,:\,A }{ C\;\vdash\;e_1\;e_2\;:\;B }

Simply-Typed Lambda Calculus

:- set_prolog_flag(occurs_check,true).
:- op(500,yfx,$).

type(C,var(X),       T) :- first(X:T,C).
type(C,lam(X,E),A -> B) :- type([X:A|C], E,  B).
type(C,E1 $ E2,      B) :- type(C,E1,A->B),
                           type(C,E2,A).

first(K:V,[K1:V1|Xs]) :- K = K1, V = V1.
first(K:V,[K1:V1|Xs]) :- K\==K1, first(K:V, Xs).
?- type([], lam(x,var(x)), A->A). % type checking
true .

?- type([], lam(x,var(x)), T).    % type inference
T = (_G123->_G123) .

?- type([], E,             A->A). % type inhabitation
E = lam(_G234,var(_G234)) .

HM = STLC + Type Poly.

:- set_prolog_flag(occurs_check,true).
:- op(500,yfx,$).

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,E1 $ E2,      B ) :- type(C,E1,A -> B), type(C,E2,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(mono(T),T).
instantiate(poly(C,T),T1) :- copy_term(t(C,T),t(C,T1)).
  • Type binding X:A in STLC corresponds to X:mono(A) in HM
  • poly(C,A) is a type scheme of A closed under the context C
  • Instationation implemented by Prolog's built-in copy_term

HM = STLC + Type Poly.

:- set_prolog_flag(occurs_check,true).
:- op(500,yfx,$).

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,E1 $ E2,      B ) :- type(C,E1,A -> B), type(C,E2,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(mono(T),T).
instantiate(poly(C,T),T1) :- copy_term(t(C,T),t(C,T1)).
?- copy_term(t([],A->B), t([],T)).     
T = (_G993->_G994).  % fresh vars: _G993, _G994 for A, B 

?- copy_term(t([x:A],A->B), t([x:A],T)).  
T = (A->_G1024).     % fresh vars: _G1024 for B only

Type Constructor Polymorphism

(a.k.a. higher-kinded polymorphism)

  -- 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 List a  -- varying number of children
type List a = [a]
(1)~\forall X^{*}.X \to X
(1) X.XX(1)~\forall X^{*}.X \to X
(2)~\forall X^{*}.\forall Y^{*}.\forall F^{*\to*}.(X \to Y) \to F\,X \to F\,Y
(2) X.Y.F.(XY)FXFY(2)~\forall X^{*}.\forall Y^{*}.\forall F^{*\to*}.(X \to Y) \to F\,X \to F\,Y

HM only supports type polymorphism such as (1) but
not higher-kinded poly. such as (2) supported in Haskell

HM + TyCon Poly.

:- set_prolog_flag(occurs_check,true).
:- op(500,yfx,$).

kind(KC, var(X), K) :- first(X:T,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),       T1) :- first(X:T,C), instantiate(T,T1).
type(KC,C,lam(X,E), A -> B) :- type([X:mono(A)|C],E,B), kind(KC,A->B,o).
type(KC,C,E1 $ E2,      B ) :- type(KC,C,E1,A -> B), type(KC,C,E2,A).
type(KC,C,let(X=E0,E1), T ) :- type(KC,C,E0,A),
                               type(KC,[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(mono(T),T).
instantiate(poly(C,T),T1) :- copy_term(t(C,T),t(C,T1)).

(a specification which kind of works but not really ...)

Note. Not a correct spec. for TyCon Poly. but just for demo.
            The "instantiate" predicate should be modified too.

HM + TyCon Poly.

(a specification which kind of works but not really ...)

?- type(KC, [], lam(x,var(x)), T).       
KC = [_G1578:_G1581|_G1584],   
T = (var(_G1578)->var(_G1578)) .
% OK, got the most general type at first :)

?- type(KC, [], lam(x,lam(y,var(x))), T).
KC = [_G1598:_G1601|_G1604],
T = (var(_G1598)->var(_G1598)->var(_G1598)) ;
KC = [_G1598:_G1601, _G1598:_G1612|_G1618],
T = (var(_G1598)->var(_G1598)->var(_G1598)) ;
KC = [_G1598:_G1601, _G1598:_G1612|_G1618],
T = (var(_G1598)->var(_G1598)->var(_G1598)) ;
KC = [_G1598:_G1601, _G1609:_G1612|_G1618],
T = (var(_G1609)->var(_G1598)->var(_G1609))
% Well, the most general type in 4th solution :(

gets even more unpleasant for HM + TyCon Poly + Kind Poly + ...

Workaround for extensions of HM

  • The Problem happens because we have
    two different expectations for Type Variables
    • in typeunifiable logic variables 
    • in kindatomic variables
  • A Workaround
    • Delay kind goals until type goals are resolved
      - used DCG to delay kind goals during type infer and call them later after some preprocessing
    • For further details see our paper
    • Prolog Specs available online at TIPER homepage
  • Prolog has features to exploit for workarounds but it is not ideal as a specification language for type systems 

Lessons from our work

  • Logic Programming can be effective for
    Executable Relational Specifications of type systems
     
  • Prolog is not a perfect tool for this purpose. It lacks 
    • search strategy suitable for type inference problems
    • resolving goals at conceptually different levels with possibly different set of unification variables
    • order irrelevant unification to properly support specifications for extensible records with Row Poly.
    • may be neat to have lazy coinductive resolution

Outline

  • Introduction

  • Relational Specification

  • Logic Programming

  • Preliminary Results using Prolog

  • TIPER project - Progress & Plans

  • Related Work

(a) Experiment / Research

  • Develop type system specs
    using off-the-shelf LP tools
  • Identify limitations of existing LP tools for type system spec.
  • Investigate theories to help overcome those limitations

(b) Tool Design & Impl.

  • Parser integration
  • Error handling
  • Frontend: surface lang. with Prolog-like syntax 
  • Backend: portable LP eDSL or kernel (e.g. μKanren) to target possibly multiple language environments

Activities of the TIPER Project

continuous integration by multiple iterations of (a) and (b)

Progress / Ongoing Work

  • Polymorphic Type System Specifications using Prolog
    • HM + Type Constructor Poly. + Kind Poly. + etc.
    • HM + TyCon Poly. + Kind Poly. + Row Poly
      • type inference with extensible records
      • inference only, not supporting type annotation
  • Exploring microKanren
  • Investigating ingredients for Extensible Records

Plans for TIPER

  • Features to support
    • Polymorphisms over Type / Type Constructor / Kind
    • Extensible Records with Row Polymorphism
    • First-Class Polymorphism and Modules
    • Some FL features such as Type Classes and GADTs
    • Some OOPL features such as subtyping
  • SW Architecture
    • Prolog-like syntax frontend (or something better?)
    • parser integration & error handling
    • portable backend (LP kernel implemented as eDSL)
  • Theories to investigate
    • alternative resolution semantics (e.g., coinductive)
    • better/flexible search strategies
    • handling extra-logical features (e.g., fresh var. gen.)

Outline

  • Introduction

  • Relational Specification

  • Logic Programming

  • Preliminary Results using Prolog

  • TIPER project - Progress & Plans

  • Related Work

Related Work

  • Embedded DSL for LP

    • miniKanren and microKanren ( http://miniKanren.org ) ported to more than a dozen of programming languages

  • Coinductive flavors of LP

    • Type Inference by Coinductive Logic Programming
      (Ancona, Lagorio, Zucca 2009)   in post TYPES 2008

    • Proof Relevant Corecursive Resolution  in FLOPS 2016
      (Fu, Komendantskaya, Schrijvers, Pond 2016) 

  • Delimited Continuations for Prolog    in ICLP 2013 
    (Schrijvers, Demoen, Desouter, Wielemaker 2013)

  • Membership-Constraints and Complexity in Logic Programming with Sets  (Stolzenburg 1996)  in FroCoS 1996

Prior attempts in similar spirit

  • Executable Specification of Static Semantics   note: Typol
    (Despeyroux. 1984)  in Semantics of Data Types 1984

  • Extraction of Strong Typing Laws from Action Semantics Definitions (Doh, Schmidt. 1992)   in ESOP 1992

  • Type Inference with Constrained Types              note: HM(X)
    (Odersky, Sulzmann, Wehr. 1999)   TAPOS, 5(1):33-55

  • Type System for the Massses    in Onward 2015
    (Grewe, Erdweg, Wittmann, Mezini 2015)

  • And there are more frameworks for type checker development

    • TyS: a framework to facilitate the dev. of OO type checkers

    • Typical: Taking the Tedium Out of Typing

Conclusion

  • There have been
    • practical work on automated dev. of type checkers
      • mostly for lang. with no parametric polymorphism
      • sometimes demo HM example for inference showcase
    • automating "HM + constraints" at a pedagogical level but not including more adv. features (TyCon poly, Row poly.)
  • What we want is
    •  to build a practical framework
    • automate development of type checking & inference
    • support advanced polymorphic features 

Executable Relational Specifications of Polymorphic Type Systems using Prolog

By 안기영 (Ahn, Ki Yung)

Executable Relational Specifications of Polymorphic Type Systems using Prolog

Talk slides for FLOPS 2016

  • 2,501