We’ve seen a few functions that “return” any type
undefined :: a
error :: String -> aThese functions throw language-level exceptions
Control.Exception as follows:import Control.ExceptionControl.Exception gives you access to the following symbols:class (Typeable e, Show e) => Exception e where ...
throw :: Exception e => e -> a
throwIO :: Exception e => e -> IO a
catch :: Exception e => IO a -> (e -> IO a) -> IO a{-# LANGUAGE DeriveDataTypeable #-}
import Control.Exception
import Data.Typeable
data MyError = MyError String deriving (Show, Typeable)
instance Exception MyError
catcher :: IO a -> IO (Maybe a)
catcher action = fmap Just action `catch` handler
where handler (MyError msg) = do putStrLn msg; return Nothing*Main> catcher $ readFile "/dev/null"
Just ""
*Main> catcher $ throwIO $ MyError "something bad"
something bad
Nothing
DeriveDataTypeable language pragma (later lecture)handler’s type cannot be inferred (use constructor or type signature)
e@(SomeException _) catches all exceptionscatcher around an IO actionthrow exceptions in pure code, yet catch them only in IO
(error "one") + (error "two")?catch is restricted to the IO MonadIn IO, use throwIO (not throw) to make exception sequencing precise
do x <- throwIO (MyError "one") -- this exception thrown
y <- throwIO (MyError "two") -- this code not reached
return $ x + ythrow only where you can’t use throwIOPure exceptions quite useful for errors & unimplemented code, E.g.:
-- Simplified version of functions in standard Prelude:
error :: String -> a
error a = throw (ErrorCall a)
undefined :: a
undefined = error "Prelude.undefined"Consider the following function
pureCatcher :: a -> IO (Maybe a)
pureCatcher a = (a `seq` return (Just a))
`catch` \(SomeException _) -> return NothingpureCatcher $ 1 + 1
Just 2
*Main> pureCatcher $ 1 `div` 0
Nothing
*Main> pureCatcher (undefined :: String)
NothingWhat happens if you do this?
*Main> pureCatcher (undefined:undefined :: String)Consider the following function
pureCatcher :: a -> IO (Maybe a)
pureCatcher a = (a `seq` return (Just a))
`catch` \(SomeException _) -> return NothingpureCatcher $ 1 + 1
Just 2
*Main> pureCatcher $ 1 `div` 0
Nothing
*Main> pureCatcher (undefined :: String)
NothingWhat happens if you do this?
*Main> pureCatcher (undefined:undefined :: String)
Just "*** Exception: Prelude.undefinedConsider the following function
pureCatcher :: a -> IO (Maybe a)
pureCatcher a = (a `seq` return (Just a))
`catch` \(SomeException _) -> return NothingpureCatcher $ 1 + 1
Just 2
*Main> pureCatcher $ 1 `div` 0
Nothing
*Main> pureCatcher (undefined :: String)
NothingWhat happens if you do this?
*Main> pureCatcher (undefined:undefined :: String)
Just "*** Exception: Prelude.undefinedcatch only catches exceptions when thunks actually evaluated!
Evaluating a list does not evaluate the head or tail
*Main> seq (undefined:undefined) ()
()
(:) or [])Exercise: Force evaluation of every element of a list
seq-like function with the following signature, that evaluates every element of list before evaluating second argumentseqList :: [a] -> b -> b*Main> seqList [1, 2, 3] ()
()
*Main> seqList [1, 2, 3, undefined] ()
*** Exception: Prelude.undefinedseqList :: [a] -> b -> b
seqList [] b = b
seqList (a:as) b = seq a $ seqList as bdeepseq in library of same name that does this for many common data typestry returns Right a normally, Left e if an exception occurred
try :: Exception e => IO a -> IO (Either e a)finally and onException run a clean-up action
finally :: IO a -> IO b -> IO a -- cleanup always
onException :: IO a -> IO b -> IO a -- after exceptionb) is discardedcatchJust catches only exceptions matching a predicate on value
catchJust :: Exception e =>
(e -> Maybe b) -> IO a -> (b -> IO a) -> IO a
readFileIfExists f = catchJust p (readFile f) (\_ -> return "")
where p e = if isDoesNotExistError e then Just e else Nothing*Main> readFileIfExists "/nosuchfile"
""
*Main> readFileIfExists "/etc/shadow"
*** Exception: /etc/shadow: openFile: permission denied ...IO actions
IO monadIO also can’t catch exceptionsMaybe Monad, where can use Nothing to indicate failureinstance Monad Maybe where
(Just x) >>= k = k x
Nothing >>= _ = Nothing
return = Just
fail _ = Nothingfail method called when bind pattern matches fail in do block*Main> (do 1 <- return 2; return 3) :: Maybe Int
NothingHaskell implements user-level threads in Control.Concurrent
forkIO call creates a new thread:
forkIO :: IO () -> IO ThreadId -- creates a new threadA few other very useful thread functions:
throwTo :: Exception e => ThreadId -> e -> IO ()
killThread :: ThreadId -> IO () -- = flip throwTo ThreadKilled
threadDelay :: Int -> IO () -- sleeps for # of µsec
myThreadId :: IO ThreadIdIO action, or abort after # of µsec
System.Timeout has a slightly better version of this functiondata TimedOut = TimedOut UTCTime deriving (Eq, Show, Typeable)
instance Exception TimedOut
timeout :: Int -> IO a -> IO (Maybe a)
timeout usec action = do
-- Create unique exception val (for nested timeouts):
expired <- fmap TimedOut getCurrentTime
ptid <- myThreadId
let child = do threadDelay usec
throwTo ptid expired
parent = do ctid <- forkIO child
result <- action
killThread ctid
return $ Just result
catchJust (\e -> if e == expired then Just e else Nothing)
parent
(\_ -> return Nothing)MVarsThe MVar type lets threads communicate via shared variables
MVar t is a mutable variable of type t that is either full or emptynewEmptyMVar :: IO (MVar a) -- create empty MVar
newMVar :: a -> IO (MVar a) -- create full MVar given val
takeMVar :: MVar a -> IO a
putMVar :: MVar a -> a -> IO ()MVar is full, takeMVar makes it empty and returns former contentsMVar is empty, putMVar fills it with a valueMVar or putting a full one puts thread to sleep until MVar becomes availableMVarMVar callstryTakeMVar :: MVar a -> IO (Maybe a) -- Nothing if empty
tryPutMVar :: MVar a -> a -> IO Bool -- False if fullimport Control.Concurrent
import Control.Exception
import Control.Monad
pingpong :: Bool -> Int -> IO ()
pingpong v n = do
mvc <- newEmptyMVar -- MVar read by child
mvp <- newEmptyMVar -- MVar read by parent
let parent n | n > 0 = do when v $ putStr $ " " ++ show n
putMVar mvc n
takeMVar mvp >>= parent
| otherwise = return ()
child = do n <- takeMVar mvc
putMVar mvp (n - 1)
child
tid <- forkIO child
parent n `finally` killThread tid
when v $ putStrLn ""*Main> pingpong True 10
10 9 8 7 6 5 4 3 2 1
import Criterion.Main
...
main :: IO ()
main = defaultMain [
bench "thread switch test" $ whnfIO mybench
]
where mybench = pingpong False 10000$ ghc -O pingpong.hs
[1 of 1] Compiling Main ( pingpong.hs, pingpong.o )
Linking pingpong ...
$ ./pingpong
...
benchmarking thread switch test
...
mean 3.809 ms (3.750 ms .. 3.868 ms)
...
-threaded to allow OS threads (pthread_create) as wellforkOS call creates Haskell thread bound to a new OS thread
forkOS :: IO () -> IO ThreadId-threaded, initial thread is boundWhoa… what happened? -threaded 27 times slower?
$ rm pingpong
$ ghc -threaded -O pingpong.hs
Linking pingpong ...
$ ./pingpong
...
mean 101.1 ms (99.48 ms .. 101.9 ms)
...
-threaded, all Haskell threads run in one OS thread
-threaded introduces multiple OS-level threads
unbound haskell threads have same performance as w/o -threadedforkIO to make it unboundwrap :: IO a -> IO a
wrap action = do
mv <- newEmptyMVar
_ <- forkIO $ (action >>= putMVar mv) `catch`
\e@(SomeException _) -> putMVar mv (throw e)
takeMVar mvrunInUnboundThreadsafe and unsafe-threaded, GHC ensures safe FFI calls run in separate OS threadunsafe FFI calls from unbound threads can block other threadspthread_getspecific can get confused if called from a migrated unbound threadforkOnSome handy MVar utility functions for updating a value
modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b
modifyMVar_ :: MVar a -> (a -> IO a) -> IO ()modifyMVar x (\n -> return (n+1, n))” like “x++” in CHow would you implement modifyMVar?
modifyMVar :: MVar a -> (a -> IO (a,b)) -> IO b
modifyMVar m action = do
v0 <- takeMVar m
(v, r) <- action v0 `onException` putMVar m v0
putMVar m v
return rthrowTo, killThread)Some handy MVar utility functions for updating a value
modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b
modifyMVar_ :: MVar a -> (a -> IO a) -> IO ()modifyMVar x (\n -> return (n+1, n))” like “x++” in CHow would you implement modifyMVar?
modifyMVar :: MVar a -> (a -> IO (a,b)) -> IO b
modifyMVar m action = do
v0 <- takeMVar m -- -------------- oops, race condition
(v, r) <- action v0 `onException` putMVar m v0
putMVar m v
return rkillThread on the current thread while current thread between takeMVar and onExceptiontimeout and wrap functions from a few slides ago have same problemThe mask function can sidestep such race conditions
mask :: ((forall a. IO a -> IO a) -> IO b) -> IO bRankNTypes. For now, ignore “forall a.”—just makes function more flexiblemask $ \f -> b runs action b with asynchronous exceptions maskedf allows exceptions to be unmasked again for an actiontakeMVar)Example: Fixing modifyMVar
modifyMVar :: MVar a -> (a -> IO (a,b)) -> IO b
modifyMVar m action = mask $ \unmask -> do
v0 <- takeMVar m -- automatically unmasked while waiting
(v, r) <- unmask (action v0) `onException` putMVar m v0
putMVar m v
return rforkIO preserves the current mask state
unmask function in child threadwrap functionwrap :: IO a -> IO a -- Fixed version of wrap
wrap action = do
mv <- newEmptyMVar
mask $ \unmask -> do
tid <- forkIO $ (unmask action >>= putMVar mv) `catch`
\e@(SomeException _) -> putMVar mv (throw e)
let loop = takeMVar mv `catch` \e@(SomeException _) ->
throwTo tid e >> loop
loopunmask in parent thread
loop will sleep on takeMVar, which implicitly unmasksbracket functionmask is tricky, but library function bracket simplifies use
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO cExample: process file without leaking handle
bracket (openFile "/etc/mtab" ReadMode) -- first
hClose -- last
(\h -> hGetContents h >>= doit) -- mainExample: fix parent function from our timeout example
parent = do ctid <- forkIO child -- old code,
result <- action -- bad if async
killThread ctid -- exception
return $ Just result parent = bracket (forkIO child) killThread $ -- new code
\_ -> fmap Just actionMVarsMVars work just fine as a mutex:
-- type introduces type alias (like typedef in C)
type Mutex = MVar ()
mutex_create :: IO Mutex
mutex_create = newMVar ()
mutex_lock, mutex_unlock :: Mutex -> IO ()
mutex_lock = takeMVar
mutex_unlock mv = putMVar mv ()
mutex_synchronize :: Mutex -> IO a -> IO a
mutex_synchronize mv action =
bracket (mutex_lock mv) (\_ -> mutex_unlock mv)
(\_ -> action)Mutex if it is locked
MutexUse full MVar rather than empty to mean lock held
type Mutex = MVar ThreadId
mutex_create :: IO Mutex
mutex_create = newEmptyMVar
mutex_lock, mutex_unlock :: Mutex -> IO ()
mutex_lock mv = myThreadId >>= putMVar mv
mutex_unlock mv = do mytid <- myThreadId
lockTid <- tryTakeMVar mv
unless (lockTid == Just mytid) $
error "mutex_unlock"ThreadId of lock owner in MVarmask for this question…data Cond = Cond (MVar [MVar ()])
cond_create :: IO Cond
cond_create = liftM Cond $ newMVar []
-- liftM is fmap for Monads (though today all Monads are Functors anyway):
-- liftM f m1 = do x <- m1; return (f m1)
cond_wait :: Mutex -> Cond -> IO ()
cond_wait m (Cond waiters) = do
me <- newEmptyMVar
modifyMVar_ waiters $ \others -> return $ others ++ [me]
mutex_unlock m -- note we don't care if preempted after this
takeMVar me `finally` mutex_lock m
cond_signal, cond_broadcast :: Cond -> IO ()
cond_signal (Cond waiters) = modifyMVar_ waiters wakeone
where wakeone [] = return []
wakeone (w:ws) = putMVar w () >> return ws
cond_broadcast (Cond waiters) = modifyMVar_ waiters wakeall
where wakeall ws = do mapM_ (flip putMVar ()) ws
return []MVars inside MVars is very powerfulControl.Concurrent.Chan provides unbounded channels
MVars – for read and and write end of Streamdata Item a = Item a (Stream a)
type Stream a = MVar (Item a)
data Chan a = Chan (MVar (Stream a)) (MVar (Stream a))data Item a = Item a (Stream a)
type Stream a = MVar (Item a)
data Chan a = Chan (MVar (Stream a)) (MVar (Stream a))
newChan :: IO (Chan a)
newChan = do
empty <- newEmptyMVar
liftM2 Chan (newMVar empty) (newMVar empty)
-- liftM2 is like liftM for functions of two arguments:
-- liftM2 f m1 m2 = do x1 <- m1; x2 <- m2; return (f x1 x2)
writeChan :: Chan a -> a -> IO ()
writeChan (Chan _ w) a = do
empty <- newEmptyMVar
modifyMVar_ w $ \oldEmpty -> do
putMVar oldEmpty (Item a empty)
return empty
readChan :: Chan a -> IO a
readChan (Chan r _) =
modifyMVar r $ \full -> do
(Item a newFull) <- takeMVar full
return (newFull, a)High-level Stream (TCP & Unix-domain) socket support in Network
connectTo :: HostName -> PortID -> IO Handle
listenOn :: PortID -> IO Socket
accept :: Socket -> IO (Handle, HostName, PortNumber)
sClose :: Socket -> IO ()
hClose :: Handle -> IO ()Exercise: Network-enabled rock-paper-scissors. Define:
withClient :: PortID -> (Handle -> IO a) -> IO aThis accepts connection, plays single game, exits
*Main> withClient (PortNumber 1617) (computerVsUser Rock)
$ nc localhost 1617
Please enter one of [Rock,Paper,Scissors]
Rock
You Tiewget cs240h.stanford.edu/rock2solution.hs
bracket in appropriate places to make it exception safewithClient :: PortID -> (Handle -> IO a) -> IO a
withClient listenPort fn =
bracket (listenOn listenPort) sClose $ \s -> do
bracket (accept s) (\(h, _, _) -> hClose h) $
\(h, host, port) -> do
putStrLn $ "Connection from host " ++ host
++ " port " ++ show port
fn hBuild a program netrock that plays two users against one another and exits after one game
$ nc localhost 1617
Please enter one of [Rock,Paper,Scissors]
Rock
You Win
$ nc localhost 1617
Please enter one of [Rock,Paper,Scissors]
Scissors
You LoseStart here: wget cs240h.stanford.edu/netrock.hs, implement:
netrock :: PortID -> IO ()You may find it useful to define and use:
play :: MVar Move -> MVar Move
-> (Handle, HostName, PortNumber) -> IO ()
play myMoveMVar opponentMoveMVar (h, host, port) = doIf your OS is missing nc: wget cs240h.stanford.edu/netcat.hs
play :: MVar Move -> MVar Move
-> (Handle, HostName, PortNumber) -> IO ()
play myMoveMVar opponentMoveMVar (h, host, port) = do
putStrLn $ "Connection from host " ++ host ++ " port " ++ show port
myMove <- getMove h
putMVar myMoveMVar myMove
opponentMove <- takeMVar opponentMoveMVar
let o = outcome myMove opponentMove
hPutStrLn h $ "You " ++ show o
netrock :: PortID -> IO ()
netrock listenPort =
bracket (listenOn listenPort) sClose $ \s -> do
mv1 <- newEmptyMVar
mv2 <- newEmptyMVar
let cleanup mv (h,_,_) = do
tryPutMVar mv (error "something blew up")
hClose h
wait <- newEmptyMVar
forkIO $ bracket (accept s) (cleanup mv1) (play mv1 mv2)
`finally` putMVar wait ()
bracket (accept s) (cleanup mv2) (play mv2 mv1)
takeMVar waitAlso have low-level BSD socket support in Network.Socket
socket :: Family -> SocketType -> ProtocolNumber -> IO Socket
connect :: Socket -> SockAddr -> IO ()
bindSocket :: Socket -> SockAddr -> IO ()
listen :: Socket -> Int -> IO ()
accept :: Socket -> IO (Socket, SockAddr)getAddrInfo looks up hostnames just like [RFC3493] (returns [AddrInfo])getAddrInfo :: Maybe AddrInfo
-> Maybe HostName -> Maybe ServiceName
-> IO [AddrInfo]SockAddr for talking to web server:webServerAddr :: String -> IO SockAddr
webServerAddr name = do
addrs <- getAddrInfo Nothing (Just name) (Just "www")
return $ addrAddress $ head $ addrsnetcat :: String -> String -> IO ()
netcat host port = do
-- Extract address from first AddrInfo in list
AddrInfo{ addrAddress = addr, addrFamily = family }:_
<- getAddrInfo Nothing (Just host) (Just port)
-- Create a TCP socket connected to server
s <- socket family Stream 0
connect s addr
-- Convert socket to handle
h <- socketToHandle s ReadWriteMode
hSetBuffering h NoBuffering -- THIS IS IMPORTANT
-- Punt on complex locale stuff
hSetBinaryMode stdout True
-- Copy data back and forth taking advantage of laziness
done <- newEmptyMVar
forkIO $ (hGetContents h >>= putStr) `finally` putMVar done ()
getContents >>= hPutStr h
takeMVar done