Implementing Linear Haskell

Matthew Pickering & Arnaud Spiwack

Linear Haskell: a Summary

Linearity on the arrow

a \rightarrow b\\ a \multimap b

type of linear functions

can be any type

\( f \) linear
\(\iff\)

\(f u\) consumed once \(\Rightarrow\) \(u\) consumed once

Linearity, not uniqueness

Backwards compatible

Multiplicity polymorphism

a \rightarrow_\pi b

\( a \rightarrow b  = a \rightarrow_\omega b\)

\( a \multimap b = a \rightarrow_1 b\)

\( a \rightarrow_p b \): polymorphic

Instances

Properties

Semiring ( e.g. \(a*(b+c) = a*b + a*c\))
Ordered ( e.g. \(a\leqslant c \Rightarrow a+b\leqslant c+b\))

Joins ( i.e. \(a\vee b\leqslant c \iff a\leqslant c \wedge b\leqslant c\))

\(\pi \leqslant \omega \)

Typing rules

\cfrac { \Gamma \vdash f : \alpha \rightarrow_{\color{blue}\pi} \tau \quad \Delta \vdash u : \alpha } { \Gamma \mathbin{\color{darkorange}+} {\color{blue}\pi} \mathbin{\color{darkorange}*} \Delta \vdash f\,u : \tau }
\cfrac { \Gamma, x:_{\color{blue}\pi} \alpha \vdash u : \tau } { \Gamma \vdash \lambda x:_{\color{blue}\pi} \alpha. u : \alpha \rightarrow_{\color{blue}\pi} \tau }

A type-checking algorithm

\cfrac { \Gamma \vdash f : \alpha \rightarrow_{\color{blue}\pi} \tau \leadsto U \quad \Gamma \vdash u : \alpha \leadsto V} { \Gamma \vdash f\,u : \tau \leadsto U \mathbin{\color{darkorange}+} {\color{blue}\pi} \mathbin{\color{darkorange}*} V}
\cfrac { \Gamma, x:\alpha \vdash u : \tau \leadsto (x\mapsto{\color{blue}\rho}, U) \quad {\color{blue}\rho} \mathrel{\color{darkorange}\leqslant}{\color{blue}\pi} } { \Gamma \vdash \lambda x:_{\color{blue}\pi} \alpha. u : \alpha \rightarrow_{\color{blue}\pi} \tau \leadsto U}

Multiplicity data type

data Mult
= Zero
| One
| Omega
| Mult `MultAdd` Mult
| Mult `MultMul` Mult
| MultTy Type
data Weighted a
  = Weighted Mult a
Weighted Type
Type

often

variables are mere type variables

data Multiplicity
  = Omega
  | One

Multiplicities as a type

FunTy

(\rightarrow_\cdot)
FunTyCon :: Type
FunTy :: Mult -> Type -> Type -> Type
a \rightarrow_\pi b

Arguments:

RuntimeRep*2, Mult, Type*2

Propagating to Core

data Var
  = …
  | Id {…
        varType :: Type,
        varMult :: Mult }

This is new

data Coercion
  = …
  | FunCo Role Coercion Coercion Coercion

New: multiplicity coercion

Core transformations

\def\let{\mathop{\color{teal}\mathsf{let}}} \def\in{\mathbin{\color{darkmagenta}\mathsf{in~}}} \def\mult{\color{blue}} \let x_{\mult\pi} = \let y_{\mult\rho} = u \in v \in e\\ \Longrightarrow \phantom{a}\\ \let y_{\mult\pi*\rho} = u \in \let x_{\mult\pi} = v \in e
\def\let{\mathop{\color{teal}\mathsf{let}}} \def\in{\mathbin{\color{darkmagenta}\mathsf{in~}}} \def\mult{\color{blue}} \let x_{\phantom{\mult\pi}} = \let y_{\phantom{\mult\rho}} = u \in v \in e\\ \Longrightarrow \phantom{a}\\ \let y_{\phantom{\mult\pi*\rho}} = u \in \let x_{\phantom{\mult\pi}} = v \in e

The Float Out incident

\def\let{\mathop{\color{teal}\mathsf{let}}} \def\in{\mathbin{\color{darkmagenta}\mathsf{in~}}} \def\case{\mathop{\color{teal}\mathsf{case}}} \def\of{\mathbin{\color{darkmagenta}\mathsf{of~}}} \def\mult{\color{blue}} \case u \of x_{\mult\pi}~\{ … ; pat \rightarrow \let y_{\mult\rho} = v \in e; … \} \\ \Longrightarrow \phantom{a}\\ \let y_{\mult??} = v \in \case u \of x_{\mult\pi}~\{ … ; pat \rightarrow e; … \}

(and inlining, and CSE,…)

\def\let{\mathop{\color{teal}\mathsf{let}}} \def\in{\mathbin{\color{darkmagenta}\mathsf{in~}}} \def\case{\mathop{\color{teal}\mathsf{case}}} \def\of{\mathbin{\color{darkmagenta}\mathsf{of~}}} \def\mult{\color{blue}} \case u \of x_{\phantom{\mult\pi}}~\{ … ; pat \rightarrow \let y_{\phantom{\mult\rho}} = v \in e; … \} \\ \Longrightarrow \phantom{a}\\ \let y_{\phantom{\mult??}} = v \in \case u \of x_{\phantom{\mult\pi}}~\{ … ; pat \rightarrow e; … \}

Alias-like binders

\def\let{\mathop{\color{teal}\mathsf{let}}} \def\in{\mathbin{\color{darkmagenta}\mathsf{in~}}} \def\case{\mathop{\color{teal}\mathsf{case}}} \def\of{\mathbin{\color{darkmagenta}\mathsf{of~}}} \def\mult{\color{blue}} \dfrac { \Gamma \vdash u : \alpha \leadsto {\mult U} \quad \Gamma, x:_{\mult U} \alpha \vdash e: \tau \leadsto {\mult V} } { \Gamma \vdash \let x : \alpha = u \in e : \tau \leadsto {\mult V}}

Pushing coercions

\def\let{\mathop{\color{teal}\mathsf{let}}} \def\in{\mathbin{\color{darkmagenta}\mathsf{in~}}} \def\case{\mathop{\color{teal}\mathsf{case}}} \def\of{\mathbin{\color{darkmagenta}\mathsf{of~}}} \def\mult{\color{blue}} (f \rhd co)\,u \Longrightarrow (f\,(u \rhd co_{arg})) \rhd co_{res}

What happened to \(co_{mult}\)‽ Bad! bad! bad!

Only push coercion when \(co_{mult} = \mathsf{refl}\).

data Coercion
  = …
  | FunCo Role Coercion Coercion Coercion

Wrapper for all

\mathsf{Just} : \forall a. a \multimap \mathsf{Maybe}\,a
\mathsf{Just} : \forall p\,a. a \rightarrow_p \mathsf{Maybe}\,a

This is key!

When used as a term.
Crucial for backwards compatibility

"inferred": doesn't affect visible type applications

Implemented as a wrapper

Strictness analysis: CPR

$wf :: A -> (# C, D #)

f :: A -> B
f x = case $wf x of
  (# y, z #) -> B y z

Linear: \(\verb|$wf x|\) is consumed exactly once.

But what if \(\mathsf{B} : a \multimap b \rightarrow B\) ?

Then we need \(\verb|(#,#)| : a \multimap b \rightarrow \verb|(#|a,b\verb|#)|\)

Later! For now, deactivate CPR when not all fields linear

https://github.com/tweag/ghc/tree/linear-types

https://ghc.haskell.org/trac/ghc/wiki/LinearTypes/Implementation

https://github.com/ghc-proposals/ghc-proposals/pull/111

Made with Slides.com