Arnaud Spiwack

Joint with: Jean-Philippe Bernardy, Richard Eisenberg, Csongor Kiss, Ryan Newton, Simon Peyton Jones, Nicolas Wu

## … until GHC 9.0

 a ⊸ b

Since GHC 9.0

{-# LANGUAGE LinearTypes #-}

Completely normal Haskell + an extra type

(+ stuff for polymorphism)

(but we won't talk about it today)

## Consume exactly once

f :: A ⊸ B
 f u
 u

If            is consumed exactly once
then      is consumed exactly once

What does “consume exactly once” mean?

evaluate x

apply x and consume the result exactly once

decompose x and consume both components exactly once

Base type

Function

Pair

## Linear types, by examples (1/3)

id x = x

linear

dup x = (x,x)

not linear

swap (x,y) = (y,x)

linear

forget x = ()

not linear

## Linear types, by examples (2/3)

f (Left x) = x
f (Right y) = y

linear

linear

not linear

h x b = case b of
True -> x
False -> x
g z = case z of
Left x -> x
Right y -> y
k x b = case b of
True -> x
False -> ()

linear

## Linear types, by examples (3/3)

f x = dup x

linear

not linear

h u = u 0
g x = id (id x)
k u = u (u 0)

linear

not linear

## Application class 1

Making more things pure

Example: safe mutable arrays

## Mutable arrays: the ST way

array :: Int -> [(Int,a)] -> Array a
array size pairs = runST $do fma <- newMArray size forM pairs (write ma) return (unsafeFreeze ma) newMArray :: Int -> ST s (MArray s a) read :: MArray s a -> Int -> ST s a write :: MArray s a -> (Int, a) -> ST s () unsafeFreeze :: MArray s a -> ST s (Array a) forM :: Monad m => [a] -> (a -> m ()) -> m () runST :: (∀s. ST s a) -> a Allocate Fill Freeze  unsafeFreeze is unsafe! ## The same, in Linear Haskell array :: Int -> [(Int,a)] -> Array a array size pairs = newMArray size$ \ma ->
freeze (foldl write ma pairs)
newMArray :: Int -> (MArray a ⊸ Ur b) ⊸ Ur b
write     :: MArray a ⊸ (Int,a) -> MArray a
read      :: MArray a ⊸ Int -> (MArray a, Ur a)
freeze    :: MArray a ⊸ Ur (Array a)
foldl     :: (a ⊸ b ⊸ a) -> a ⊸ [b] ⊸ a

Allocate

Fill

Freeze (safe!)

## Scope passing style

newMArray :: Int -> (MArray a ⊸ Ur b) ⊸ Ur b

This is what ensures that references to arrays are unique

## Unrestricted

data Ur a where
Ur :: a -> Ur a

compare with

data Id a where
Id :: a ⊸ Id a

Data types are linear by default

## Scope passing style (continued)

newMArray :: Int -> (MArray a ⊸ Ur b) ⊸ Ur b

Don't work:

newMArrayDirect :: Int ⊸ MArray a
newMArrayLeaky :: Int -> (MArray a ⊸ b) ⊸ b

If the result is consumed exactly once
then the argument is consumed exactly once

Remember

## Application class 2

Protocols in types

Example: files

## I/O protocols

Files

• ensure you close a file
• ensure no read after close

Malloc

• ensure you free a block
• ensure no read after close

Sockets

• ensure bind a socket before reading from it
• ensure you close it
• ensure you don’t read or bind after close

## Files

openFile  :: FilePath -> IOL Handle
readLine  :: Handle ⊸ IOL (Handle, Ur String)
closeFile :: Handle ⊸ IOL ()
firstLine :: FilePath -> IOL (Ur String)
firstLine fp = do
h <- openFile fp
(h, Ur xs) <- readLine h
closeFile h
return \$ Ur xs

do { x <- u ; v} = u >>= \x -> v
(>>=) :: IOL a ⊸ (a ⊸ IOL b) ⊸ IOL b

## Application (class) 3

Circuits and such

## Drawing circuits with linear types

let f (a,b) =
let (x,y,z) = 𝜙 a in
let (t,w) = 𝜔 (y,b) in
(𝜉 (x,t), 𝜁 (z,w))
in
decode f

https://slides.com/aspiwack/loria202205

https://www.tweag.io/blog/tags/linear-types/

\chi

https://arxiv.org/abs/1710.09756

\chi

https://arxiv.org/abs/2103.06195

\chi

https://arxiv.org/abs/2103.06127