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

Implementing Linear Haskell

By Arnaud Spiwack

Implementing Linear Haskell

As the linear type proposal is underway, and the specifics of how linear types should be exposed in Haskell are being debated, you may wonder: but how is it implemented? Is it easy? Is it hard? Is it small? Is it big? As a GHC developer what would it change for me in practice? This talk will describe our implementation in progress of the linear type feature. How we changed the linear arrow constructor, how type checking is performed, what changes are necessary in Core to account for linearity, etc…

  • 1,793