Modular implicits for OCaml how to assert success. Gallium Seminar,

Similar documents
Modular implicits. c Leo White, Frédéric Bour & Jeremy Yallop

Adding GADTs to OCaml the direct approach

Tracing Ambiguity in GADT Type Inference

First-Class Type Classes

The design of a programming language for provably correct programs: success and failure

From Types to Sets in Isabelle/HOL

User-Defined Algebraic Data Types

The Design of Core C++ (Notes)

Derek Dreyer. WG2.8 Meeting, Iceland July 16-20, 2007

Some Advanced ML Features

ML 4 Unification Algorithm

Parametricity. Leo White. February Jane Street 1/ 70

Where is ML type inference headed?

l e t print_name r = p r i n t _ e n d l i n e ( Name : ^ r. name)

Lecture #23: Conversion and Type Inference

Overloading, Type Classes, and Algebraic Datatypes

Conversion vs. Subtyping. Lecture #23: Conversion and Type Inference. Integer Conversions. Conversions: Implicit vs. Explicit. Object x = "Hello";

A system of constructor classes: overloading and implicit higher-order polymorphism

Simple Unification-based Type Inference for GADTs

Soundness and principality of type inference for structural polymorphism

Let Arguments Go First

Goal. CS152: Programming Languages. Lecture 15 Parametric Polymorphism. What the Library Likes. What The Client Likes. Start simpler.

Monads. Prof. Clarkson Fall Today s music: Vámanos Pal Monte by Eddie Palmieri

Explicit Recursion in Generic Haskell

CSCI-GA Scripting Languages

Advances in Programming Languages

Generic polymorphism on steroids

Proving OCaml s type system? Jacques Garrigue Nagoya University

Coq with Classes. Matthieu Sozeau. Journées PPS 2011 September 5th 2011 Trouville, France. Project Team πr 2 INRIA Paris

COS 320. Compiling Techniques

Simplifying and Improving Qualified Types

Subtyping. Lecture 13 CS 565 3/27/06

CSE 3302 Programming Languages Lecture 8: Functional Programming

c constructor P, Q terms used as propositions G, H hypotheses scope identifier for a notation scope M, module identifiers t, u arbitrary terms

Coq quick reference. Category Example Description. Inductive type with instances defined by constructors, including y of type Y. Inductive X : Univ :=

Structural polymorphism in Generic Haskell

TYPE INFERENCE. François Pottier. The Programming Languages Mentoring ICFP August 30, 2015

GADTs meet Subtyping

DEFINING A LANGUAGE. Robert Harper. Friday, April 27, 12

Lecture Overview. [Scott, chapter 7] [Sebesta, chapter 6]

Formal Semantics. Prof. Clarkson Fall Today s music: Down to Earth by Peter Gabriel from the WALL-E soundtrack

CSE 130, Fall 2006: Final Examination

CSE-321: Assignment 8 (100 points)

Type Inference with Inequalities

Abstraction. Leo White. February Jane Street 1/ 88

To What Extend Is Type Inference for Parameteric Polymorphism Possible in the Presence of Ad-Hoc and Subtype Polymorphism

Lambda Calculus and Type Inference

Overview. Declarative Languages D7012E. Overloading. Overloading Polymorphism Subtyping

Code reuse through polymorphic variants

Type structure is a syntactic discipline for maintaining levels of abstraction John Reynolds, Types, Abstraction and Parametric Polymorphism

Agenda. CS301 Session 11. Common type constructors. Things we could add to Impcore. Discussion: midterm exam - take-home or inclass?

Dialects of ML. CMSC 330: Organization of Programming Languages. Dialects of ML (cont.) Features of ML. Functional Languages. Features of ML (cont.

Dependent Object Types - A foundation for Scala s type system

INF 212 ANALYSIS OF PROG. LANGS Type Systems. Instructors: Crista Lopes Copyright Instructors.

3.4 Deduction and Evaluation: Tools Conditional-Equational Logic

Static Checking and Type Systems

The Substitution Model

Simon Peyton Jones Microsoft Research August 2013

λ calculus is inconsistent

Computing Fundamentals 2 Introduction to CafeOBJ

An update on XML types

Introduction to Programming, Aug-Dec 2006

Data Types The ML Type System

Introduction to Objective Caml

Why OCaml? Introduction to Objective Caml. Features of OCaml. Applications written in OCaml. Stefan Wehr

Modules Matter Most. Robert Harper Carnegie Mellon University. MacQueen Fest Chicago May 2012

Programming Languages Assignment #7

IA014: Advanced Functional Programming

COMP 4161 NICTA Advanced Course. Advanced Topics in Software Verification. Toby Murray, June Andronick, Gerwin Klein

Type Processing by Constraint Reasoning

Variables. Substitution

COMP 181. Agenda. Midterm topics. Today: type checking. Purpose of types. Type errors. Type checking

Type Checking and Type Inference

Qualified Types for MLF

Lecture 3: Recursion; Structural Induction

Programming Languages and Techniques (CIS120)

Programming Language Concepts: Lecture 19

Let Arguments Go First

Background. CMSC 330: Organization of Programming Languages. Useful Information on OCaml language. Dialects of ML. ML (Meta Language) Standard ML

Polymorphic Algebraic Data Type Reconstruction

Two Problems. Programming Languages and Compilers (CS 421) Type Inference - Example. Type Inference - Outline. Type Inference - Example

Introduction to ML. Mooly Sagiv. Cornell CS 3110 Data Structures and Functional Programming

CS558 Programming Languages

OCaml Data CMSC 330: Organization of Programming Languages. User Defined Types. Variation: Shapes in Java

Typed Racket: Racket with Static Types

Harvard School of Engineering and Applied Sciences Computer Science 152

Inductive Definitions, continued

Introduction to Haskell

Lambda Calculus and Type Inference

First-class modules: hidden power and tantalizing promises

Programming Languages Lecture 15: Recursive Types & Subtyping

CSE 341 Section 5. Winter 2018

Gradual Parametricity, Revisited

L3 Programming September 19, OCaml Cheatsheet

CSE 130: Programming Languages. Polymorphism. Ranjit Jhala UC San Diego

Topic 16: Issues in compiling functional and object-oriented languages

CSci 450: Org. of Programming Languages Overloading and Type Classes

CSE-321 Programming Languages 2012 Midterm

Diagnosing Haskell Type Errors

The Typed λ Calculus and Type Inferencing in ML

Transcription:

Modular implicits for OCaml how to assert success Jacques Garrigue Nagoya University Frédéric Bour Sponsored by Jane Street LLC Gallium Seminar, 14-03-2016

Garrigue & Bour Mdular implicits and success 1/26 Modular implicits for OCaml A proposal by Bour, White and Yallop [2014/2015] Apply Scala implicits to OCaml Modules and functors can be made implicit Can express basic Haskell type classes, and many extensions Static implicit scope, no global uniqueness restriction Emphasis on non-ambiguity of implicit resolution

Garrigue & Bour Mdular implicits and success 2/26 Basic type class module type Show = sig type t val show : t -> string end let show {M : Show} x = M.show x val show : {M : Show} -> M.t -> string implicit module Show_int = struct type t = int let show = string_of_int end implicit module Show_float = struct type t = float let show = string_of_float end show 1 (* uses Show_int *) - : string = "1" show 1.0 (* uses Show_float *) - : string = "1.0"

Garrigue & Bour Mdular implicits and success 3/26 Implicit abstraction Abstraction is explicit, and registers an implicit module. let print {S : Show} (x : S.t) = print_endline (show x) (* uses S *) val print : {S : Show} -> S.t -> unit print (1+1) (* uses Show_int *) 2 print (1.0 + 2.0) (* uses Show_float *) 3.0

Garrigue & Bour Mdular implicits and success 4/26 Implicit functor = evidence construction implicit module Show_pair {A : Show} {B : Show} = struct type t = A.t * B.t let show (a,b : t) = "(" ^ A.show a ^ "," ^ B.show b ^ ")" end implicit module Show_list {X : Show} = struct type t = X.t list let show (xs : t) = "[" ^ String.concat "; " (List.map X.show xs) ^ "]" end print [("hello", 1); ("world", 2)] (* uses Show_list(Show_pair(Show_string,Show_int)) *) (* : Show with type t = (string * int) list *) [("hello", 1); ("world", 2)]

Garrigue & Bour Mdular implicits and success 5/26 Constructor classes module type Monad = sig type a t val return : a -> a t val bind : a t -> ( a -> b t) -> b t end let return {M : Monad} x = M.return x val return : {M : Monad} -> a -> a M.t let (>>=) {M : Monad} m k = M.bind m k val ( >>= ) : {M : Monad} -> a M.t -> ( a -> b M.t) -> b M.t (return 3 : int list) - : int list = [3]

Garrigue & Bour Mdular implicits and success 6/26 Associated types An implicit signature with multiple types can encode a relation between types. module type Array = sig type t and elt val create : int -> elt -> t val get : t -> int -> elt val set : t -> int -> elt -> unit end implicit module Array_Bytes = struct type t = Bytes.t and elt = char let create = Bytes.make let get = Bytes.get let set = Bytes.set end

Garrigue & Bour Mdular implicits and success 7/26 Implicit arguments Implicits can also be defined only locally, to express Haskell-style implicit arguments. module type S = sig type state val gensym : unit -> int end let gensym {M : S} () = M.gensym () val gensym : {M : S} -> unit -> int let name_val {M : S} expr body = (* M becomes implicit *) let name = gensym () in (* uses M *) Let (name, expr, body name) val name_val : {M : S} -> expr -> (int -> expr) -> expr module G : S =... (* not declared as implicit *) name_val {G} (Cst 3) (fun x -> Var x) - : expr = Let (7, Cst 3, Var 7)

Garrigue & Bour Mdular implicits and success 8/26 Virtual implicit arguments new! If a module contains only type members, and we can infer all its type definitions, then we do not need to declare a concrete definition for it. implicit module Show_poly_list {virtual X : sig type t end} = struct type t = X.t list let show (l : t) = "[" ^ string_of_int (List.length l) ^ " elements]" end print [1;2;3] [3 elements]

Garrigue & Bour Mdular implicits and success 9/26 The problem at hand We want to find all possible combinations of implicit modules and implicit functor applications that match an implicit function application. If there is only one solution, use this (extended) module path as argument to the implicit function, and check again its correctness. If there is no solution, or if there are more than one solution (ambiguity), the search fails.

Garrigue & Bour Mdular implicits and success 10/26 Specification of the search An intermediate state of the search can be seen a partial extended module path, with named holes or, seen as a tree, M = Show list(show pair(a)(b)) A : Show B : Show N = Show pair(a)(b) : Show M = Show list(n) : Show together with a constraint which these holes should satisfy: M.t = (string int)list N.t = string int A.t = string B.t = int

Garrigue & Bour Mdular implicits and success 11/26 The constraints at work In order to express module subtyping, we need type equalities (including universal type variable, for definitions) instantiation constraints, for value fields The constraints contain both flexible type constructors, corresponding to types declared in implicit module parameters of implicit functions and functors, and also type variables of the implicit function application fixed type constructors and universal variables, corresponding to types defined outside, and parameters of type declarations

Garrigue & Bour Mdular implicits and success 12/26 Incompleteness Implicit functors may express complex relations between their input and output. Whether proof search will terminate seems undecidable in general. We introduce a termination criterion for applying an implicit functor at a leaf in the tree. If the criterion is not satisfied, we block it until constraints introduced by other nodes make it satisfiable. If some blocked nodes remain, we must assume ambiguity.

Garrigue & Bour Mdular implicits and success 13/26 Higher-order unification Parametric flexible type constructors hint at the need for higher-order unification. (return 3 : int list) int M.t = int list This has two solutions, type a t = int list and type a t = a list. Since higher-order unification is known to be undecidable, we shall stick to the simpler approach of dynamic higher-order pattern unification, i.e. only substitute a parametric flexible when all its parameters are distinct type variables. Trying M = Monad list, i.e. type a t = a list: α M.t = α list int M.t = int list α M.t = α list int list = int list α M.t = α list

Garrigue & Bour Mdular implicits and success 14/26 Principality of constraint solving In order to make the result of proof search independent of search order, we need to have constraint solving commute with node filling. In particular, we need the following 2 properties. Deriving a contradiction from a constraint is monotonous Ambiguity reduces with the addition of constraints Resolving flexible type definitions into rigid ones is monotonous Satisfiable termination constraints and solvable virtual implicit parameters increase with the addition of constraints

Garrigue & Bour Mdular implicits and success 15/26 Model theoretic approach Usually proofs of unification model solutions by substitutions However, we need to delay constraints on parametric flexible types, which may have no concrete solution Intuitive solution: define a model where parametric flexible types are relations rather than functions Consequence: parametric flexible types should not appear in equations, as they would break transitivity, or incur a high cost Example: int t = int int t = bool

Garrigue & Bour Mdular implicits and success 16/26 Types and constraints Types τ ::= α parametric variable t( τ) datatype constructors v variable v ::= φ flexible type x unification variable Constraints ψ ::= Φ( τ, x) flexible constraint e ψ ψ other cases e ::= τ 1 =... = τ n multi-equation

Garrigue & Bour Mdular implicits and success 17/26 Translation into constraints We can translate a type containing parametric flexible types into the combination of type containing unification variables and a conjunction of flexible constraints. T (α) = (α, ) T (t( τ)) = (t( τ ), ψ) where ( τ, ψ) = unzip(map T τ) T (φ) = (φ, ) T (Φ( τ)) = (x, Φ( τ, x) ψ) where ( τ, ψ) = unzip(map T τ) and x is fresh We can also encode instantiation constraints. α 1.τ 1 α 2.τ 2 [ x/ α 1 ]τ 1 = τ 2

Garrigue & Bour Mdular implicits and success 18/26 Constraint solving rules merge v = e v = e ψ v = e = e ψ decompose t(τ 1,..., τ n ) = t(τ 1,..., τ n ) = e ψ τ 1 = τ 1... τ n = τ n t(τ 1,..., τ n ) = e ψ promote φ = t(τ 1,..., τ n ) = e ψ φ = t(φ 1,..., φ n ) = e φ 1 = τ 1... φ n = τ n ψ fu(t(τ 1,..., τ n )) define clash α = t( τ) = e ψ Φ( α, x) x = τ = e ψ Φ( α, x) x = τ = e [Φ( α, z) z = τ]ψ subst x = τ = e ψ x = τ = [τ/x]e [τ/x]ψ fu(τ) = scope φ = τ = e ψ fp(τ) clash α = α = e ψ fu(τ) = fp(τ) α cycle ψ ψ v > v α α clash t( τ) = u( τ ) = e ψ t u scope Φ( α, x) x = τ = e ψ fp(τ) α

Garrigue & Bour Mdular implicits and success 19/26 Model We model flexible types, unification variables, and flexible constraints using the following 3 valuation functions. v f : S(φ) {τ T fp(τ) = } v u : S(x) {s T s } v c : S(Φ) {R T n P(T ) bool ατ 1 σ, R( α, σ) τ 1 σ fp(τ 1 ) α (σ = {τ 1 } σ τ, R( τ, σ ) σ = {[ τ/ α]τ 1 })} where T = {τ fv(τ) = } fp(τ) = free parametric variables of τ fu(τ) = free unification variables of τ fv(τ) = free flexible types and unification variables of τ

Garrigue & Bour Mdular implicits and success 20/26 Interpretation [[α]] = {α} [[t(τ 1,..., τ n )]] = {t(τ 1,..., τ n ) τ 1 [[τ 1]],..., τ n [[τ n]]} [[φ]] = {v f (φ)} [[x]] = v u (x) [[τ 1 =... = τ n ]] = ([[τ 1 ]] = [[τ 2 ]])... ([[τ 1 ]] = [[τ n ]]) [[Φ(τ 1,..., τ n, x)]] = {v c (Φ)(τ 1,..., τ n, [[x]]) τ 1 [[τ 1]],..., τ n [[τ n]]}

Garrigue & Bour Mdular implicits and success 21/26 Properties Theorem 1 Each constraint rewriting rule preserves the set of solutions. Theorem 2 If ψ is a normal form constraint (i.e. applying define and subst do not change it, and none of the other rules apply), then it admits a valuation (v f, v u, v c ) such that [[ψ]] = true, and v u (x) is a singleton only if we have an equation x = τ = e in ψ, and fu(τ) =. Corollary 1 If ψ is in normal form, and contains τ = e, then fu(τ) = iff for any valuation satisfying ψ, [[τ]] is a singleton. Moreover, the translation of [[τ]] is the same {τ } for any valuation satisfying ψ iff for all flexible variable φ in fv(τ), and each minimal variable such that ψ φ > v (or v = φ itself if it is minimal), there is an equation v = τ = e with fv(τ ) = in ψ. Theorem 3 There is a rewriting strategy that reaches a normal form in a finite number of steps.

Garrigue & Bour Mdular implicits and success 22/26 Global minimality Minimality is a weaker version of principality, where we assume that all polymorhic values are given the most general type Dependent function calls can be made minimal by usual techniques (cf. polymorphic methods) Ambiguity criterion: either there are two concrete solutions, or there exists a partial blocked solution: A partial tree matching the constraints, such that all leaves are blocked. Combined, this means that, assuming minimality of the environment, satisfying the ambiguity criterion is monotonous (less polymorphism means less ambiguity), and the solution found is unique.

Garrigue & Bour Mdular implicits and success 23/26 Design decisions Local ambiguity or global ambiguity Global ambiguity requires backtracking, can be costly Ignore the type of value fields or not Selecting only on the base of type fields, and names of value fields, provides a simpler mental model Termination criterion Should use the known part of types, but there is some freedom Where to lauch constraint resolution Currently done when generalizing flexible types, allowing to handle simultaneously multiple implicit applications Hiding/overriding mechanism Sometimes we need to hide an implicit module/functor to avoid ambiguity Introduce some priorities, to avoid ambiguity?

Garrigue & Bour Mdular implicits and success 24/26 The diamond problem Signature inheritance leads to ambiguity. module type Eq = sig type t val equal : t -> t -> bool end implicit module Eq_int = struct type t = int val equal (x : int) y = (x=y) end module type Cmp = sig include Eq val compare : t -> t -> int end implicit module Cmp_int = struct include Eq_int val compare x y = (x-y) end Both Eq int and Cmp int are subtypes of Eq with type t = int.

Garrigue & Bour Mdular implicits and success 25/26 Practical solution: blessing signatures Add an abstract type component to disambiguate signatures. module type Eq = sig type eq type t val equal : t -> t -> bool end module type Cmp = sig type cmp include Eq with type eq := cmp (* just erases eq *) val compare : t -> t -> int end This blessing seems more meaningful than the types of value fields, and allows to simplify resolution.

Garrigue & Bour Mdular implicits and success 26/26 Conclusion A very expressive framework Proof search can be made principal However, ambiguity is monotonous in a contravariant way, so that the type system cannot be principal The resulting system is well-behaved: minimal and symmetric Numerous design decisions