Download (λx. From x Functions to x Types) Higher-Order

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Falcon (programming language) wikipedia , lookup

Intuitionistic type theory wikipedia , lookup

Standard ML wikipedia , lookup

C Sharp (programming language) wikipedia , lookup

Transcript
(λx. From x Functions to x Types)
Higher-Order
Páli Gábor János
[email protected]
Eötvös Loránd University
Department of Programming Languages and
Compilers, Faculty of Informatics
3rd Winter School for
PhD Students (WSPS3)
January 16, 2016
Programming with Types in Haskell
Levels of Type Definitions
Type synonym. A different name for an already existing type of
composition of such types.
type Bool = Int
Derived type. Definition of a type isomorphic to an already
existing type.
newtype Bool = B Int
-- B :: Int -> Bool, a conversion function
mkBool :: Int -> Bool -- smart constructor
mkBool n | 0 <= n && n <= 1 = B n
| otherwise = error "Invalid value"
Algebraic Data Type (ADT). Makes it possible to control how
the elements of the type can be constructed.
data Bool = True | False
-- True, False :: Bool, data constructors
[3..47]
Composing Custom Types Algebraically
Every Haskell type can be expressed with the following types:
Unit :: ? – unit (1)
data Unit = Unit
Pair :: ? → ? → ? – product (×)
data Pair a b = Pair a b
Either :: ? → ? → ? – sum or coproduct (+)
data Either a b = Left a | Right b
Fix :: (? → ?) → ? – fixed point (µ)
data Fix f = Fix (f (Fix f))
An example of using all these combinators:
List a = µ(λxs.1 + (a × xs))
type List a = Fix (L a)
data L a xs = L (Either Unit (Pair a xs))
or more typically:
data List a = Nil | Cons a (List a)
[4..47]
Types for Representing Information
Algebraic data types can be used to model enumerations:
data Bool = True | False
(&&) :: Bool -> Bool -> Bool
True && True = True
_
&& _
= False
as well as data structures:
data Tree a = L | B a (Tree a) (Tree a)
aTree :: Tree Char
aTree = B ’a’ (B ’b’ L (B ’c’ L L)) L
size :: Tree a -> Integer
size L
= 0
size (B _ lt rt) = 1 + size lt + size rt
[5..47]
Types for Functions without Computation
Meet the Peano numbers:
Nat :: ? – N
Zero :: Nat – 0
Succ :: Nat → Nat – S
data Nat = Zero | Succ Nat
aNat :: Nat
aNat = Succ (Succ (Succ Zero))) -- S (S (S 0))
They do not immediately correspond to natural numbers, they
have to be specifically interpreted like that:
fromNat :: Nat -> Integer
fromNat Zero
= 0
fromNat (Succ n) = 1 + fromNat n
aNat 6→∗β 3, but fromNat aNat →∗β 3
[6..47]
Types for Hiding Functions
Difference list is a function that returns a stored list appended
to its parameter.
data DiffList a = DFL ([a] -> [a])
It enables prepending and appending elements to the list in
constant time.
[7..47]
Types for Hiding Functions
-- http://hackage.haskell.org/package/dlist
fromList :: [a] -> DiffList a
fromList dl = DFL (\xs -> dl ++ xs)
apply :: DiffList a -> [a] -> [a]
apply (DFL f) xs = f xs
toList :: DiffList a -> [a]
toList df = apply df []
empty :: DiffList a
empty = DFL (\xs -> xs)
singleton :: a -> DiffList a
singleton x = DFL (\xs -> x : xs)
[8..47]
Types for Hiding Functions
infixr ‘cons‘
cons :: a -> DiffList a -> DiffList a
x ‘cons‘ (DFL f) = DFL ((x:) . f)
infixl ‘snoc‘
snoc :: DiffList a -> a -> DiffList a
(DFL f) ‘snoc‘ x = DFL (f . (x:))
append :: DiffList a -> DiffList a -> DiffList a
append (DFL f) (DFL g) = DFL (f . g)
concat :: [DiffList a] -> DiffList a
concat = Data.List.foldr append empty
[9..47]
Types for Hiding Functions
replicate :: Int -> a -> DiffList a
replicate n x = DFL (\xs ->
let f m | m <= 0
= xs
| otherwise = x : f (m - 1) in
f n)
foldr :: (a -> b -> b) -> b -> DiffList a -> b
foldr f b = Data.List.foldr f b . toList
map :: (a -> b) -> DiffList a -> DiffList b
map f = foldr (cons . f) empty
[10..47]
Encoding Invariants with Types
Alternating lists:
data AList a b = Nil | Cons a (AList b a)
alist :: AList Int Char
alist = Cons 1 (Cons ’A’ (Cons 2 (Cons ’B’ Nil)))
Cyclic lists:
{-# LANGUAGE EmptyDataDecls #-}
data Void
data Maybe a = Nothing | Just a
data CList a b
= Var b | Nil
1
| C a (CList (Maybe b))
1
2
2
3
clist1, clist2 :: CList Int Void
clist1 = C 1 (C 2 (Var Nothing))
clist2 = C 1 (C 2 (C 3[11..47]
(Var (Just Nothing))))
Typing with Types
data Expr
= ValI Integer
| ValB Bool
| Add Expr Expr
| And Expr Expr
| Eq Expr Expr
| If Expr Expr Expr
eval :: Expr -> Either Integer Bool
eval (ValI n) = Left n
eval (ValB b) = Right b
eval (x ‘Add‘ y) = case (eval x, eval y) of
(Left m , Left n) -> Left (m + n)
eval (x ‘And‘ y) = case (eval x, eval y) of
(Right a, Right b) -> Right (a && b)
eval (x ‘Eq‘ y) = case (eval x, eval y) of
(Left m , Left n) -> Right (m == n)
(Right m, Right n) -> Right (m == n)
eval (If x y z) = case (eval x) of
Right b -> if b then (eval y) else (eval z)
[12..47]
Typing with Types
Actually the Haskell compiler might be persuaded to do some
static type checking for us:
{-# LANGUAGE GADTs, KindSignatures #-}
data Expr :: * -> * where
ValI :: Integer -> Expr Integer
ValB :: Bool -> Expr Bool
Add :: Expr Integer -> Expr Integer -> Expr Integer
And :: Expr Bool -> Expr Bool -> Expr Bool
Eq
:: (Eq a) => Expr a -> Expr a -> Expr Bool
If
:: Expr Bool -> Expr a -> Expr a -> Expr a
eval
eval
eval
eval
eval
eval
eval
:: (Eq a) => Expr a ->
(ValI n)
= n
(ValB b)
= b
(x ‘Add‘ y) = (eval x)
(x ‘And‘ y) = (eval x)
(x ‘Eq‘ y) = (eval x)
(If x y z) = if (eval
a
+ (eval y)
&& (eval y)
== (eval y)
x) then (eval y) else (eval z)
Hence eval becomes a total function.
[13..47]
Attaching Contexts with Types
-- Data.Functor (Prelude)
class Functor (f :: * -> *) where
fmap :: (a -> b) -> (f a -> f b)
-- Control.Applicative
infixl 4 <$>
(<$>) = fmap
infixl 4 <*>
class (Functor f) => Applicative (f :: * -> *) where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
x
f
Functor laws:
Fx
v
f
==
==
id
fmap f . fmap g
pure id <*> v
pure (.) <*> u <*> v <*> w
pure f <*> pure x
u <*> pure y
==
==
==
==
(identity)
(composition)
v
u <*> (v <*> w)
pure (f x)
pure ($ y) <*> u
[14..47]
u
z
g
y
fmap id
fmap (f . g)
n
m
u
z
g
y
m
n
v
(identity)
(composition)
(homomorphism)
(interchange)
Attaching Contexts with Types
type Name = String
type Env = [(Name, Integer)]
newtype Expr a = E (Env -> a)
var :: Name -> Expr Integer
var n = E (\e ->
case (lookup n e) of
Just v -> v
_
-> error ("variable is not defined: " ++ show n))
cond :: Expr Bool -> Expr a -> Expr a -> Expr a
cond b x y = E (\e -> if (eval b e) then (eval x e) else (eval y e))
bind :: Name -> Expr Integer -> Expr a -> Expr a
bind n x body = E (\e -> eval body ((n, eval x e) : e))
instance Functor Expr where
fmap f x = pure f <*> x
instance Applicative Expr where
pure x = E (\_ -> x)
f <*> x = E (\e -> (eval f e) (eval x e))
eval :: Expr a -> (Env -> a)
eval (E expr) = expr
[15..47]
Attaching Contexts with Types
gcd :: Expr Integer
gcd =
cond ((==) <$> var a <*> var b)
(var a)
(cond ((>) <$> var a <*> var b)
(bind a ((-) <$> var a <*> var b) gcd)
(bind b ((-) <$> var b <*> var a) gcd))
where [a,b] = ["a","b"]
So, for example:
eval gcd [("a", 113), ("b", 56)] →∗β 1
eval gcd [("a", 56), ("b", 98)] →∗β 14
eval gcd [("a", 42), ("b", 42)] →∗β 42
[16..47]
Fear Not the Monad
Fear Not the Monad: Introduction
The bad news: Understading monads are a must to speak
contemporal Haskell.
The good news: They are not that complicated at all!
[18..47]
Fear Not the Monad: Introduction
The number of monad tutorials over the years
https://byorgey.wordpress.com/2009/01/12/
abstraction-intuition-and-the-monad-tutorial-fallacy/
[19..47]
Fear Not the Burrito
[20..47]
Fear Not the Burrito
[21..47]
Fear Not the Burrito
[22..47]
Fear Not the Burrito
[23..47]
Fear Not the Burrito
[24..47]
Fear Not the Burrito
[25..47]
Fear Not the Burrito
[26..47]
What a Monad is not?
All of the following claims is false:
I
Monads are not purely functional.
I
Monads model effects in programs.
I
Monads are about state.
I
Monads implement sequencing statements.
I
Monads model I/O.
I
Monads need lazy evaluation.
I
Monads help us to use tricky side effects.
I
Monads are an embedded language in Haskell.
I
Monads can be only comprehended by mathematicians.
[27..47]
Understanding the Monads in 8 Simple Steps
1. Do not read monad tutorials.
2. Do not read monad tutorials.
3. Learn about Haskell types.
4. Learn to use type classes.
5. Study the Typeclassopedia∗ .
6. Study the definition of monads.
7. Program with monads.
8. Do not write monad tutorials.
∗
http://www.cs.tufts.edu/comp/150FP/archive/
brent-yorgey/tc.pdf
[28..47]
Programming with Monads: Structuring Programs
Assume that we would like to implement the following program
in a purely functional language:
I
Print to the console that "Provide me a word> ".
I
Read the input from the user.
Depending of the value of the input:
I
I
I
If it is a palindrome, print that "Palindrome."
If it is not a palindrome, print that "Not a palindrome."
Given:
putStr :: String -> World -> World
getLine :: World -> (String, World)
[29..47]
Programming with Monads: Structuring Programs
($) :: (a -> b) -> a -> b
f $ x = f x
program :: World -> World
program w =
(\(s, w) ->
if (s == reverse s)
then putStr "A palindrome." w
else putStr "Not a palindrome." w) $
getLine $
putStr "Provide me a word> " $ w
[30..47]
Programming with Monads: Structuring Programs
(|>) :: a -> (a -> b) -> b
x |> f = f x
program’ :: World -> World
program’ w = w |>
putStr "Provide me a word> " |>
getLine |> \(s, w) ->
if (s == reverse s)
then putStr "A palindrome." w
else putStr "Not a palindrome." w
[31..47]
Programming with Monads: Structuring Programs
type IO a = World -> (a, World)
-- putStr :: String -> IO ()
-- getLine :: IO String
(>>=) :: IO a -> (a -> IO b) -> IO b
(f >>= g) w = (g x) w’ where (x, w’) = f w
(>>) :: IO a -> IO b -> IO b
f >> g = f >>= \_ -> g
program’’ :: IO ()
program’’ =
putStr "Provide me a word> " >>
getLine >>= \s ->
if (s == reverse s)
then putStr "A palindrome."
else putStr "Not a palindrome."
[32..47]
Programming with Monads: Structuring Programs
main :: IO ()
main = do
putStr "Provide me a word> "
s <- getLine
if (s == reverse s)
then putStr "A palindrome."
else putStr "Not a palindrome."
[33..47]
Programming with Monads: The Monad Class
-- Control.Monad
class Monad (m :: * -> *) where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>)
:: m a -> m b -> m b
x >> f = x >>= \_ -> f
fail
:: String -> m a
fail s = error s
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
f >=> g = \x -> f x >>= g
return >=> f
f >=> return
(f >=> g) >=> h
===
===
===
f
f
f >=> (g >=> h)
+ a run function
[34..47]
Programming with Monads: The do Syntax
Rewriting rules:
do { e1; e2 }
-->
e1 >> do { e2 }
do { p <- e1; e2 }
-->
e1 >>= \x -> case x of p -> do { e2 }
_ -> fail "error"
do { let p = e1; e2 }
-->
let p = e1 in do { e2 }
do { e }
-->
e
For example:
do {
do
x <- f;
x <- f
y <- g;
y <- g
z <- h;
-->
z <- h
-->
let t = (x,y,z);
let t = (x,y,z)
return t
return t
}
[35..47]
f >>= \x ->
g >>= \y ->
h >>= \z ->
let t = (x,y,z) in
return t
Every Monad is a Functor
Consider the following connection between functors and
monads:
join :: Monad m => m (m a) -> m a
join x = x >>= id
x >>= f = join (fmap f x)
fmap f
pure x
mf <*>
do {
pure f
do {
fmap
x
=== pure f <*> x
=== return x
mx
===
f <- mf; x <- mx; return (f x) }
<*> mx ===
f’ <- return f; x <- mx; return (f’ x) } ===
f x
Starting from GHC 7.10, it is mandatory to make any monad a
functor.
[36..47]
IO, the Most Infamous Monad
data IO a = ?
With the IO monad, it is possible to model arbitrary side effect,
that is a "jolly joker".
For example:
I
putStrLn: Print to the standard output.
I
getLine: Read from the standard input.
I
IORef: Mutable references, "pointers".
I
IOError: Exception handling.
I
IOArray: Efficient, mutable arrays.
I
forkIO: Concurrent threads.
The run function: the run-time system, unsafePerformIO
[37..47]
State: Stateful Variables (Intuition)
type State s a = s -> (a, s)
-(s -> (a, s)) -> (a -> (s -> (b, s))) -> (s -> (b, s))
(>>=) :: State s a
-> (a -> State s b)
-> State s b
(x >>= f) s = (f v) s’
where (v, s’) = x s
-a -> (s -> (a, s))
return :: a -> State s a
(return x) s = (x, s)
-s -> (s, s)
get :: State s s
(get) s = (s, s)
-s -> ((), s)
put :: s -> State s ()
(put x) s = ((), x)
-(s -> (a, s)) -> s -> (a, s))
runState :: State s a
-> s -> (a, s))
runState f s = f s
[38..47]
State: Stateful Variables
-- Control.Monad.State
newtype State s a = S (s -> (a, s))
runState :: State s a -> s -> (a, s)
runState (S f) x = f x
instance Monad (State s) where
return x = S (\s -> (x,s))
x >>= f = S (\s ->
let (x’, s’) = runState x s in
runState (f x’) s’)
get :: State s s
get = S (\s -> (s, s))
put :: s -> State s ()
put x = S (\_ -> ((), x))
[39..47]
State: Stateful Variables (Example)
data Tree a = Node a (Tree a) (Tree a) | Leaf
deriving Show
numberTree :: (Eq a) => Tree a -> Tree Int
numberTree t = fst (runState (numberTreeM t) [])
numberTreeM :: (Eq a) => Tree a -> State [a] (Tree Int)
numberTreeM Leaf
= return Leaf
numberTreeM (Node x lt rt) = do
table <- get
let (table’, pos) = case (Data.List.findIndex (== x) table) of
Just i -> (table, i)
_
-> (table ++ [x], length table)
put table’
lt’ <- numberTreeM lt
rt’ <- numberTreeM rt
return (Node pos lt’ rt’)
[40..47]
Maybe: Optional Values
data Maybe a = Just a | Nothing
instance Monad Maybe where
return x = Just x
(Just x) >>= f = f x
_
>>= _ = Nothing
fail _ = Nothing
For example:
safeDiv :: Integer -> Integer -> Maybe Integer
_ ‘safeDiv‘ 0 = Nothing
x ‘safeDiv‘ y = Just (x ‘div‘ y)
averageDeviation :: [Integer] -> Maybe Integer
averageDeviation xs = do
let average xs = (sum xs) ‘safeDiv‘ (toInteger $ length xs)
mean <- average xs
average [ abs (x - mean) | x <- xs ]
[41..47]
List: Nondeterminism
data [a] = [] | a:[a]
instance Monad [] where
return x = [x]
xs >>= f = concat [ f x | x <- xs ]
fail _
= []
For example:
multiplyToNM :: Integer -> [(Integer, Integer)]
multiplyToNM n = do
x <- [1..n]
y <- [x..n]
if (x * y == n)
then return (x,y)
else fail "Not applicable."
multiplyToN n = [ (x,y) | x <- [1..n], y <- [x..n], x * y == n ]
[42..47]
Writer: Logging
-- Control.Monad.Writer
newtype Writer w a = W (a, w)
runWriter :: (Monoid w) => Writer w a -> (a, w)
runWriter (W (x, w)) = (x, w)
instance (Monoid w) => Monad (Writer w) where
return x = W (x, mempty)
x >>= f
= W (let (x’, w) = runWriter x
in
let (x’’, w’) = runWriter (f x’) in
(x’’, w <> w’))
tell :: (Monoid w) => w -> Writer w ()
tell x = W ((), x)
censor :: (Monoid w) => (w -> w) -> Writer w a -> Writer w a
censor f (W (x, w)) = W (x, f w)
[43..47]
Writer: Logging with Monoid
-- Data.Monoid
class Monoid (a :: *) where
mempty :: a
mappend :: a -> a -> a
(<>) :: (Monoid a) => a -> a -> a
(<>) = mappend
-- GHC 7.6+
Monoid laws:
mempty <> x
x <> mempty
x <> (y <> z)
mconcat
===
===
===
===
x
x
(x <> y) <> z
foldr (<>) mempty
For example:
instance Monoid [a] where
mempty = []
mappend = (++)
-- data Sum a = Sum a
instance (Num a) => Monoid (Sum a) where
mempty
= Sum 0
(Sum x) ‘mappend‘ (Sum y) = Sum (x + y)
[44..47]
Writer: Logging (Example)
gcdWithLog :: Int -> Int -> (Int, [String])
gcdWithLog x y = runWriter (censor reverse (gcdWithLogM x y))
gcdWithLogM :: Int -> Int -> Writer [String] Int
gcdWithLogM x y | y == 0 = do
tell ["Finished with " ++ show x]
return x
gcdWithLogM x y = do
result <- gcdWithLogM y (x ‘mod‘ y)
let msg = show x ++ " mod " ++ show y ++ " = " ++ show (x ‘mod‘ y)
tell [msg]
return result
[45..47]
Composing Monads – Monad Transformers
countEntries :: FilePath -> K ()
countEntries path = f path 0 where
f p n = do
deepestReached <- get
when (deepestReached < n) (put n)
c <- liftIO (System.Directory.getDirectoryContents p)
let contents = filter (‘notElem‘ [".", ".."]) c
tell [(p, length contents)]
forM_ contents (\name -> do
let newName = p ++ "/" ++ name
isDir <- liftIO (System.Directory.doesDirectoryExist newName)
maxDepth <- ask
when (isDir && (n < maxDepth)) (f newName (n + 1)))
type S = Int
type C = Int
type K = ReaderT C (StateT S (WriterT [(FilePath,Int)] IO))
runStack :: K a -> Int -> IO ((a,S),[(FilePath,Int)])
runStack k max = runWriterT (runStateT (runReaderT k c) s)
where (s, c) = (0, max)
[46..47]
Stacking Monads
[47..47]