Arnaud Spiwack
Joint with: Jean-Philippe Bernardy, Richard Eisenberg, Csongor Kiss, Ryan Newton, Simon Peyton Jones, Nicolas Wu
That's about it
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)
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
id x = x
✓
linear
dup x = (x,x)
✗
not linear
swap (x,y) = (y,x)
✓
linear
forget x = ()
✗
not linear
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
f x = dup x
✓
linear
✗
not linear
h u = u 0
g x = id (id x)
k u = u (u 0)
✓
linear
✗
not linear
Making more things pure
Example: safe mutable arrays
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!
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!)
newMArray :: Int -> (MArray a ⊸ Ur b) ⊸ Ur b
This is what ensures that references to arrays are unique
data Ur a where
Ur :: a -> Ur a
compare with
data Id a where
Id :: a ⊸ Id a
Data types are linear by default
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
Protocols in types
Example: files
Files
Malloc
Sockets
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
About the type of monads see A Tale of Two Functors or: How I learned to Stop Worrying and Love Data and Control — Arnaud Spiwack
Circuits and such
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/
https://arxiv.org/abs/1710.09756
https://arxiv.org/abs/2103.06195
https://arxiv.org/abs/2103.06127