h4life: Add.

This commit is contained in:
Sergiu Ivanov 2018-09-22 23:40:20 +02:00
parent c3f9ac6aee
commit 863c210fae
16 changed files with 1510 additions and 2 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,214 @@
{- funcs.hs
This file contains some of the examples to be shown in the first class
of the course "Haskell for Life", by Sergiu Ivanov (sivanov@lacl.fr):
http://lacl.fr/~sivanov/doku.php?id=en:haskell_for_life
This file is distributed under the Creative Commons Attribution Alone
licence.-}
{- The examples shown in the first part. -}
add x y = x + y
factorial :: Int -> Int
factorial x = if x == 0
then 1
else x * factorial (x-1)
fibonacci :: Int -> Int
fibonacci n = if n == 1
then 1
else if n == 2
then 1
else fibonacci (n-1)
+ fibonacci (n-2)
myNot :: Bool -> Bool
myNot x = if x == True then False else True
myNot1 :: Bool -> Bool
myNot1 True = False
myNot1 False = True
factorial1 :: Int -> Int
factorial1 0 = 1
factorial1 n = n * factorial1 (n-1)
fibonacci1 :: Int -> Int
fibonacci1 1 = 1
fibonacci1 2 = 1
fibonacci1 n = fibonacci1 (n-1) + fibonacci1 (n-2)
fibonacci2 :: Int -> Int
fibonacci2 n | n <= 2 = 1
fibonacci2 n | n > 2 = fibonacci2 (n-1) + fibonacci2 (n-2)
myHead :: [a] -> a
myHead (h:_) = h
myTail :: [a] -> [a]
myTail (_:xs) = xs
{- The fun starts here.
This section contains implementations of the functions from Data.List
and Prelude. Our implementation of the function foo is called
myFoo.-}
-- | Check whether the list is empty.
myNull :: [a] -> Bool
myNull [] = True
myNull _ = False
-- | Compute the length of the list.
myLength :: [a] -> Int
myLength [] = 0
myLength (x:xs) = 1 + length xs
-- | Get the last element of the list.
--
-- list = myInit list ++ [myLast list]
myLast :: [a] -> a
myLast [x] = x
myLast (x:xs) = myLast xs
-- | Get all elements of the list, but the last one.
--
-- list = myInit list ++ [myLast list]
myInit :: [a] -> [a]
myInit [x] = []
myInit (x:xs) = x : myInit xs
-- | append = (++)
append :: [a] -> [a] -> [a]
append [] ys = ys
append (x:xs) ys = x:append xs ys
-- | Reverses the list.
--
-- In typical functional implementations, '(++)' needs to traverse the
-- first list entirely (cf. 'append'). So this implementation of
-- "reverse" would need to traverse the list at every recursive call.
-- The overall complexity would thus be O(n^2/2).
--
-- In Haskell, '(++)' is implemented with optimisation and may take
-- constant time to run.
badReverse :: [a] -> [a]
badReverse [] = []
badReverse (x:xs) = badReverse xs ++ [x]
-- | Reverses the list.
--
-- This implementation only traverses the list once.
myReverse :: [a] -> [a]
myReverse xs = reverse' xs []
where reverse' [] acc = acc
reverse' (x:xs) acc = reverse' xs (x:acc)
myTake :: Int -> [a] -> [a]
myTake _ [] = []
myTake 0 _ = []
myTake n (x:xs) = x:myTake (n-1) xs
myDrop :: Int -> [a] -> [a]
myDrop _ [] = []
myDrop 0 xs = xs
myDrop n (x:xs) = myDrop (n-1) xs
-- | Glues two lists together.
--
-- myZip [1,2] [3,4] == [(1,2),(3,4)]
myZip :: [a] -> [b] -> [(a,b)]
myZip [] _ = []
myZip _ [] = []
myZip (x:xs) (y:ys) = (x,y):myZip xs ys
-- | Checks whether an element is part of the list.
--
-- 'Eq a =>' requires that objects of type 'a' can be "compared".
--
-- We add two pattern guards to handle the cases when 'y == x' and
-- 'y /= x'. 'otherwise' is a condition which is always true.
myElem :: Eq a => a -> [a] -> Bool
myElem _ [] = False
myElem y (x:xs) | y == x = True
| otherwise = myElem y xs
-- | Given a function and a list, returns a list containing the
-- elements for which the function is true.
--
-- myFilter odd [1..5] == [1,3,5].
--
-- 'odd' is the function returning 'True' for odd numbers.
myFilter :: (a -> Bool) -> [a] -> [a]
myFilter _ [] = []
myFilter p (x:xs) | p x = x:myFilter p xs
| otherwise = myFilter p xs
-- | Applies a function to all elements of a list and returns the
-- results.
myMap :: (a -> b) -> [a] -> [b]
myMap _ [] = []
myMap f (x:xs) = f x:myMap f xs
myFoldr :: (a -> b -> b) -> b -> [a] -> b
myFoldr _ r0 [] = r0
myFoldr f r0 (x:xs) = f x (myFoldr f r0 xs)
{- This section contains the reimplementations of some of the
functions from Data.List and the standard Prelude using folds. Our
implementation of the function foo is called fldFoo.-}
-- | Sums up the elements of the list.
fldSum :: [Int] -> Int
fldSum xs = foldl (+) 0 xs
-- | Checks whether the given function returns 'True' for all elements
-- of the given list.
fldAll :: (a -> Bool) -> [a] -> Bool
fldAll f xs = foldl (\r x -> (f x) && r) True xs
-- | Checks whether the given function returns 'True' for at least one
-- element of the list.
fldAny :: (a -> Bool) -> [a] -> Bool
fldAny f xs = foldl (\r x -> (f x) || r) False xs
-- | Concatenates all the lists in the given list.
--
-- [[1,2],[3,4]] = [1,2,3,4]
fldConcat :: [[a]] -> [a]
fldConcat xs = foldl (++) [] xs
fldFilter :: (a -> Bool) -> [a] -> [a]
fldFilter f xs = reverse (foldl test [] xs)
where test r x | f x = x:r
| otherwise = r
fldMap' :: (a -> b) -> [a] -> [b]
fldMap' f xs = reverse (foldl (\r x -> f x:r) [] xs)
-- The clever Pentalog guy showed me this solution.
fldMap :: (a -> b) -> [a] -> [b]
fldMap f = foldr ((:) . f) []
-- Bubble sort.
bubblesort :: Ord a => [a] -> [a]
bubblesort xs = iterate bubble xs !! (length xs - 1)
where bubble [] = []
bubble [x] = [x]
bubble (x:y:xs) | x < y = x : bubble (y:xs)
| otherwise = y : bubble (x:xs)

View file

@ -0,0 +1,575 @@
{- monads.hs
This file contains the examples on monads to be shown in the third
class of the course "Haskell for Life", by Sergiu Ivanov
(sivanov@lacl.fr):
http://lacl.fr/~sivanov/doku.php?id=en:haskell_for_life
This file is meant to be read sequentially.
This file is distributed under the Creative Commons Attribution Alone
licence.-}
-- This line imports the (<>) operator from the module 'Data,Monoid'
-- (we will need faaar below for the MyWriter monad).
import Data.Monoid((<>))
-- | We will cheat and use these meaningless type synonyms.
type Person = String
type Car = String
personByName :: String -> Maybe Person
personByName p = Just p
carByPerson :: Person -> Maybe Car
carByPerson p = Just (p ++ "'s car")
model :: Car -> Maybe String
model c = Just ("model of " ++ c)
modelFromName :: String -> Maybe String
modelFromName name = do
person <- personByName name
car <- carByPerson person
model car
{- In this section we will reconstruct the Maybe monad.-}
data Perhaps a = Only a | Nope
deriving Show
instance Monad Perhaps where
-- | return :: a -> Perhaps a
--
-- This implementation is quite natural: we just put the value we
-- get inside an instance of 'Perhaps', and we cannot actually
-- implement it differently. Indeed, since we don't know anything
-- about the type 'a', we cannot construct values of that type, nor
-- can we transform them.
--
-- ATTENTION! 'return' does not abort execution of any block of code
-- (as in some other languages)! It's just a function called
-- "return".
return x = Only x
-- | (>>=) :: Perhaps a -> (a -> Perhaps b) -> Perhaps b
Nope >>= f = Nope
(Only x) >>= f = f x
-- | If you try to compile the file now, GHC will complain that there
-- is no instance for 'Applicative Perhaps'; that instance is required
-- by the definition of 'Monad'. Let's try defining one, just looking
-- at the types of the functions.
instance Applicative Perhaps where
-- | pure :: a -> Perhaps
--
-- Wait a second, that's the type of return!
pure = return
-- | (<*>) :: Perhaps (a -> b) -> Perhaps a -> Perhaps b
--
-- OK, so we have a function (perhaps), and we need to apply to
-- (perhaps) a value and then return the result (perhaps). Let's do
-- that.
--
-- Note that we don't use the fact that we work with Perhaps, since
-- only the definitions coming from our instance of 'Monad' are
-- enough. This means that we will be able to copy this definition
-- as-is for the other monads we will construct.
u <*> v = do -- This means we do our stuff within a monad.
f <- u -- Extract the function.
x <- v -- Extract the value.
return (f x) -- Feed the value to the function and pack the
-- result.
-- | Oh noes, we still cannot compile this code: no instance for
-- 'Functor Perhaps'. Well, 'Functor' seems easy enough.
instance Functor Perhaps where
-- | fmap :: (a -> b) -> Perhaps a -> Perhaps
--
-- Hey, that's simple enough!
--
-- fmap f Nope = Nope
-- fmap f (Only x) = Only (f x)
--
-- This code is specific to 'Perhaps', however. We could perhaps
-- (pun intended) re-use our definition of 'Monad' once again:
--
-- fmap f v = do
-- x <- v -- Unpack 'v'.
-- return (f x) -- Feed 'x' to f and pack back the result.
--
-- Yet, we will use the following cool definition based on the
-- definition from the instance of 'Applicative'. It's more compact
-- and is nice and simple exercise in Haskell types.
fmap f x = pure f <*> x
-- Let's check now that our 'Perhaps' is similar to 'Maybe', as we
-- wanted.
personByName' :: String -> Perhaps Person
personByName' p = Only p
carByPerson' :: Person -> Perhaps Car
carByPerson' p = Only (p ++ "'s car")
model' :: Car -> Perhaps String
model' c = Only ("model of " ++ c)
modelFromName' :: String -> Perhaps String
modelFromName' name = do
person <- personByName' name
car <- carByPerson' person
model' car
{- In this section we will reconstruct the list monad.-}
data List a = Cons a (List a) | Nil
deriving Show
-- | Concatenates two 'List's.
concat2 :: List a -> List a -> List a
concat2 Nil ys = ys
concat2 (Cons x xs) ys = Cons x (concat2 xs ys)
-- | Concatenates a 'List' of 'Lists'.
--
-- Example:
-- concatLists ( Cons (Cons 1 Nil) (Cons (Cons 2 Nil) Nil) ) == Cons 1 (Cons 2 Nil)
concatLists :: List (List a) -> List a
concatLists xs = concatAll xs Nil
where concatAll (Cons x xs) res = concatAll xs (concat2 res x)
concatAll Nil res = res
-- | Applies the function to all elements of the list and returns the
-- list of results.
mapList :: (a -> b) -> List a -> List b
mapList _ Nil = Nil
mapList f (Cons x xs) = Cons (f x) (mapList f xs)
instance Monad List where
-- | return :: a -> List a
--
-- Create a singleton list out of 'x', just like in the case of
-- 'Maybe'.
return x = Cons x Nil
-- (>>=) :: List a -> (a -> List b) -> List b
--
-- So, we should probably feed the values from 'List a' into the
-- function 'a -> List b' one by one (we don't really have any other
-- choice). Then we are going to get a bunch of 'List b'; we should
-- probably concatenate them to get one single big 'List b'.
x >>= f = concatLists (mapList f x)
-- | And for our 'Applicative' and 'Functor' instances we'll just
-- reuse the code we wrote for 'Perhaps'.
instance Applicative List where
pure = return
u <*> v = do
f <- u
x <- v
return (f x)
instance Functor List where
fmap f x = pure f <*> x
-- | Now let's see what kind of tricks one can do in the list monad.
-- We will use Haskell's built-in '[]' for convenience, but exactly
-- the same things can be done in `List'.
-- | Compute the Cartesian product of two lists.
--
-- We "unpack" first 'xs', then 'ys', and put the obtained 'x' and 'y'
-- together. Note that the bind operator '(>>=)' in the list monad
-- means that the lambda functions are applied to every element in
-- 'xs' and 'ys'. In other words, '(>>=)' is a kind of a foreach
-- loop.
cartesian :: [a] -> [b] -> [(a,b)]
cartesian xs ys = xs >>=
(\x -> ys >>=
(\y -> return (x,y)))
-- | Now, let's rewrite the same function using the do notation. In
-- this case, every "unpacking" (<-) means a loop over all elements of
-- the list. Thus, we loop over all elements of 'xs'; then, for each
-- such element, we loop over 'ys' and produce a pair '(x,y)' at each
-- iteration.
cartesian' :: [a] -> [b] -> [(a,b)]
cartesian' xs ys = do
x <- xs
y <- ys
return (x,y)
-- | Return a list of elements for which the given function returns
-- 'True'.
--
-- Note that we do not use return; instead we produce the singleton
-- list '[x]' directly. We need that to be able to write '[]' when
-- 'x' does satisfy the property.
myFilter :: (a -> Bool) -> [a] -> [a]
myFilter f xs = do
x <- xs
if f x then [x] else []
{- In this section we will reconstruct a simplified version of the
State monad.-}
-- | Our simplified state monad.
data MyState s a = MyState (s -> (a, s))
-- | This is just a getter for the only field of 'MyState'.
runState :: MyState s a -> (s -> (a, s))
runState (MyState f) s = f s
{- In the case of the 'Maybe' ('Perhaps') monad, we wanted to deal
with functions returning 'Maybe a'. In the case of state, we want to
deal with functions which get state as an argument and which return
their result _and also_ the new state. Thus, the functions we work
with have types of the form
'arg1 -> ... -> argn -> state -> (result, state)'.
Since the bind operator (>>=) will have to pass the state from one
function to another one, we cannot just define
data MyState s a = MyState (a, s)
If we did that, the bind operator (>>=) would have no way of passing
the state to the next function. With our definition of 'MyState' as
containing a field of a function type, we will be able to pass around
the state.
The next issue is that 'Monad' can only work with types having one
parameter, while 'MyState' has two. This means that we will have to
fix one of them. If we fix the parameter 'a' (which is the result of
computation), then bind (>>=) should be able to put together two
functions returning the same result, but using states of different
type, which is not what we want. Therefore, we are going to fix 's',
the type of the state.-}
instance Monad (MyState s) where
-- | return :: a -> MyState s a
-- return :: a -> (s -> (a, s))
--
-- We don't want 'return' to tinker with the state; it should just
-- put our value into the monad, nothing more. So, it will return a
-- function taking the state and returning a tuple containing 'x'
-- and _the same_ state.
return x = MyState (\s -> (x, s))
-- | (>>=) :: MyState s a -> (a -> MyState s b) -> MyState s b
--
-- Now, that's a more difficult type. Without going too much into
-- details, let's keep in mind that we want to get the result out of
-- 'MyState s a' and put it into the function which is the second
-- argument. Now, how do we get the result ('a') out of 'MyState s
-- a'? 'MyState s a' is the same thing as 's -> (a,s)', so we could
-- get an 'a' if we had an 's', which we don't seem to have...
--
-- But wait a little! We are supposed to return 'MyState s b',
-- which is actually 's -> (b,s)'; that is, we are supposed to
-- return a function! And inside this function, we can haz access
-- to an instance of 's' which we can use to run the first argument
-- ('MyState s a') and get our instance of 'a' to feed into the
-- second argument.
--
-- And yes, a let declaration is just a local binding of values,
-- like:
--
-- let x = 2
-- y = 3
-- in x + y
--
-- We can also use pattern matching in let bindings (just like in
-- GHCi).
x >>= f = MyState (\s ->
-- Run 'x' and get the tuple (result,state) in runState (f a) s'
let (a,s') = runState x s
-- Apply 'f' to 'a', and then run the resulting
-- state with 's'.
in runState (f a) s')
-- | Again, a small copy-and-paste work to get our 'Applicative' and
-- 'Functor' instances in place.
instance Applicative (MyState s) where
pure = return
u <*> v = do
f <- u
x <- v
return (f x)
instance Functor (MyState s) where
fmap f x = pure f <*> x
-- | Oh, before we get to write any functions using our 'MyState'
-- monad, we have to provide a way to access our state.
-- | Returns the current state.
--
-- Remember that unpacking (<-) in a State monad means getting the
-- first element of the tuple returned by any function in this monad.
-- Thus, the only thing 'myGet' needs to do is put the state it gets
-- as an argument into the first component of the tuple.
myGet :: MyState s s
myGet = MyState (\s -> (s, s))
-- | Sets the current state.
--
-- 'myPut' only changes the state and does not return anything
-- meaningful; we therefore make it put the empty tuple '()' (unit)
-- into the first component of the returned tuple, and we put the new
-- state into the second component of the returned tuple.
myPut :: s -> MyState s ()
myPut s' = MyState (\_ -> ((), s'))
{- We can now use MyState to implement a small database of cars and
people.-}
data CarsPeopleDB = CarsPeopleDB [Car] [Person]
deriving Show
cars :: CarsPeopleDB -> [Car]
cars (CarsPeopleDB c _) = c
people :: CarsPeopleDB -> [Person]
people (CarsPeopleDB _ p) = p
-- | This function adds a car to the database.
--
-- This function can access the database because it is in the
-- 'MyState' monad (it returns the type 'MyState <bla bla>'). It just
-- adds a car and returns nothing, that's why the '()'.
addCar :: Car -> MyState CarsPeopleDB ()
addCar c = do
db <- myGet -- Get the database.
let cs = cars db -- Get the list of cars.
ps = people db -- Get the list of people.
let db' = (CarsPeopleDB (c:cs) ps) -- Build the new database.
myPut db' -- Save the new database.
-- | Adds a person to the database.
--
-- Does the same thing as 'addCar', but is written more concisely.
addPerson :: Person -> MyState CarsPeopleDB ()
addPerson p = do
(CarsPeopleDB cs ps) <- myGet
myPut (CarsPeopleDB cs (p:ps))
-- | Checks whether a car is in the database.
--
-- Now, besides producing the new state, this function should also
-- tell us whether the car is in the database or not. That is why the
-- second argument to 'MyState' is 'Bool' — it's the answer of the
-- function.
carKnown :: Car -> MyState CarsPeopleDB Bool
carKnown c = do
(CarsPeopleDB cs _) <- myGet
return (elem c cs) -- Pack the result of checking whether 'c' is an
-- element of 'cs'.
-- | Checks whether a person is in the database.
personKnown :: Person -> MyState CarsPeopleDB Bool
personKnown p = do
(CarsPeopleDB _ ps) <- myGet
return (elem p ps)
-- | And now we can use our functions to build a real (c) (hehe)
-- database of car and people!
testCarsPeopleDB :: CarsPeopleDB
testCarsPeopleDB = let (res, db) = runState buildDB (CarsPeopleDB [] [])
in db
where buildDB = do
addPerson "John"
addPerson "Mary"
addCar "Mercedes"
addCar "Renault"
johnKnown <- personKnown "John"
if not johnKnown
then addPerson "Bruce"
else return () -- We have to do something in the else
-- branch as well, so let's just pack an
-- empty tuple.
{- In this section we will define a simplified version of the Reader
monad.-}
-- | The Reader monad is essentially the read-only State monad. Since
-- the state never changes, we do not need to deal with functions of
-- the form 's -> (a,s)', but only 's -> a' (there is no new state, it
-- is always the same).
--
-- We will use the so-called "record syntax" to define the accessor
-- for the single field of MyReader. It is really a syntactic sugar
-- for
--
-- data MyReader s a = MyReader (s -> a)
-- runReader :: MyReader s a -> (s -> a)
-- runReader (MyReader f) = f
data MyReader s a = MyReader { runReader :: s -> a
}
-- | The 'Monad' instance for 'MyReader' is defined in _exactly_ the
-- same way as for 'MyState'.
instance Monad (MyReader s) where
return x = MyReader (\s -> x)
-- | This time we don't have to deal with the new state, so things
-- are simpler. We first run 'x' with the state 's'; it returns an
-- instance of 'a'. Then we feed this result to 'f', and then run
-- the resulting reader with the same state 's'.
x >>= f = MyReader (\s -> runReader (f (runReader x s)) s )
-- | Again, some copy and paste.
instance Applicative (MyReader s) where
pure = return
u <*> v = do
f <- u
x <- v
return (f x)
instance Functor (MyReader s) where
fmap f x = pure f <*> x
-- | And a small function to tell us our read-only state. (It's
-- exactly like 'myGet', but is called differently).
myAsk :: MyReader s s
myAsk = MyReader (\s -> s)
-- myAsk = MyReader id
{- And here's a very simple and stupid example. We will write
functions generating various greetings for a name, which will be
obtained from the reader monad-}
hello :: MyReader Person String
hello = do
name <- myAsk
return $ "Hello " ++ name ++ "!" -- The $ is just a shortcut for not
-- writing parentheses.
-- return ("Hello " ++ name ++ "!")
howdy :: MyReader Person String
howdy = do
name <- myAsk
return $ "Howdy " ++ name ++ "!"
goodMorning :: MyReader Person String
goodMorning = do
name <- myAsk
return $ "Good morning " ++ name ++ "!"
-- This function produces all possible (hehe) greetings for the given
-- name.
--
-- Mind the pointfree style! The function may actually be written as
--
-- greetings name = runReader mkGreetings name
--
greetings :: String -> [String]
greetings = runReader mkGreetings
where mkGreetings = do
hl <- hello
hw <- howdy
gm <- goodMorning
return [hl,hw,gm]
{- In this section we will define a simplified version of the Writer
monad.-}
-- | The Writer monad is essentially a write-only State monad. Since
-- the functions in this monad do not need to access the state, the
-- type of the Writer monad is really really simple: just a tuple.
--
-- (Mind the record syntax.)
data MyWriter s a = MyWriter { runWriter :: (a,s) }
-- | There's a catch however: we're not really interested in a
-- write-only state: this would mean that we always get the state set
-- by the last function. That is why in the Writer monad the state is
-- actually an accumulator and, instead of changing it directly, the
-- functions in this monad append stuff to it. Therefore, the Writer
-- monad is the logger monad.
--
-- What we have just said means that we require some special
-- properties of 's' (some special structure): we should be able to
-- put together (append, concatenate) to values of type 's', like
-- lists or with strings. This property is described by the typeclass
-- 'Monoid'; types belonging to this typeclass have the function
-- 'mappend' (or '<>', which is the same thing) which can append two
-- values, and also the function 'mempty', returning the "empty"
-- element (like the empty list or the empty string).
instance (Monoid s) => Monad (MyWriter s) where
-- | This where the 'mempty' function for the type 's' comes in handy:
-- return "packs" the value "x" together with an empty log.
return x = MyWriter (x,mempty)
-- | We have to take the result out of 'x' and feed it into 'f', and
-- then put together the logs of the two.
x >>= f = let (r, w ) = runWriter $ x
(r',w') = runWriter $ f r
in MyWriter (r', w <> w')
-- | Some more instances...
instance (Monoid s) => Applicative (MyWriter s) where
pure = return
u <*> v = do
f <- u
x <- v
return $ f x
instance (Monoid s) => Functor (MyWriter s) where
fmap f x = pure f <*> x
-- | ... and a small function appending something to the log.
myTell :: Monoid s => s -> MyWriter s ()
myTell x = MyWriter ((), x)
{- And now it's time for an eccentric example! Let's make a weirdly
verbose calculator.-}
-- | The following functions carry out some operations, but also log
-- what they do to the Writer monad.
myAdd :: Double -> Double -> MyWriter [String] Double
myAdd x y = do
myTell $ [show x ++ "+" ++ show y]
return $ x + y
mySub :: Double -> Double -> MyWriter [String] Double
mySub x y = do
myTell $ [show x ++ "-" ++ show y]
return $ x - y
myMul :: Double -> Double -> MyWriter [String] Double
myMul x y = do
myTell $ [show x ++ "*" ++ show y]
return $ x * y
myDiv :: Double -> Double -> MyWriter [String] Double
myDiv x y = do
myTell $ [show x ++ "/" ++ show y]
return $ x / y
-- | Let's test our calculator.
testCalc :: (Double, [String])
testCalc = runWriter $ do
let x = 2
y <- myMul x 2
z <- mySub y 3
t <- myDiv y 10
a <- myAdd z t
return a

View file

@ -0,0 +1,229 @@
{- scanner.hs
A simple parallel (and slightly concurrent) port scanner.
Type:
scanner 1 10 '127.0.0.1[30-50]' 'www.google.com[80-1000]'
to scan the ports 30 through 50 on localhost and ports 80 through 1000
on www.google.com. The value 1 specifies the time the scanner will
wait before dropping a connection attempt. The value 10 specifies the
number of threads in the thread pool.
Only TCP connections are attempted.
This is an practical example to be shown in the fourth class of the
course "Haskell for Life", by Sergiu Ivanov (sivanov@lacl.fr):
http://lacl.fr/~sivanov/doku.php?id=en:haskell_for_life
This file is distributed under the Creative Commons Attribution Alone
licence.-}
import Network.Simple.TCP
import Control.Monad.Catch
import System.Timeout
import Control.Concurrent.STM.TChan
import Control.Concurrent.STM.TMVar
import GHC.Conc.Sync
import System.Environment (getArgs)
type Timeout = Int
-- | The possible outcomes of a connection attempt.
data Outcome = Connected | Refused | TimedOut
deriving Show
type PortNumber = Int
data ScanTarget = ScanTarget HostName PortNumber
-- | Attempts connecting to a port (service) on the given machine.
--
-- The timeout should be given in seconds.
testPort :: HostName -> PortNumber -> Timeout -> IO Outcome
testPort host port t =
-- When the connection is actually refused, an IO error is thrown,
-- and we use 'catchIOError' to handle that case. In the handling
-- routine (the lambda function) we will not care about the type of
-- the error, and just conclude that connection was refused.
catchIOError (timedConnect host port t) (\_ -> return Refused)
where
-- 'connect' calls the supplied function if connection is
-- successful (it's a lambda expression in our case). 'connect'
-- actually gives the information about the connection (the
-- socket), but we don't really care: once the lambda is called,
-- the connection succeeded and the port is open.
justConnect host port = connect host (show port) (\_ -> return Connected)
-- Aborts the attempts to connect after a given timeout.
--
-- 'timeout' returns 'Just result' if the function managed to get
-- the result, and 'Nothing otherwise'. We know that our result
-- can only be 'Connected', so no fancy pattern matching. (We may
-- also get an IO error, but that is handled above.)
timedConnect host port t = do
let t' = 1000000 * t -- 'timeout' uses microseconds.
maybeResult <- timeout t' $ justConnect host port
case maybeResult of
Just Connected -> return Connected
Nothing -> return TimedOut
-- | Runs a thread pool of the given size on the given channel
-- containing tasks; waits until the tasks are done.
--
-- All the threads in the pool read tasks from the same channel and
-- terminate when there are no more tasks in the channel. This means
-- that the channel must contain data from the very beginning,
-- otherwise the thread pool will just stop.
threadPool :: Int -> TChan a -> (a -> IO ()) -> IO ()
threadPool size chan process = do
-- 'mapM' is like 'map', but it can handle functions returning IO
-- values.
--
-- Here, the type of 'mapM' specialises to
--
-- mapM_ :: (a -> IO b) -> [a] -> IO [b]
--
-- So we do something for all numbers from 1 to 'size' (see the end
-- of this expression). What we are going to do is spawn a worker
-- thread and create a Boolean 'TMVar' into which the worker will
-- put 'True' when it sees that the channel is empty. When all
-- threads will have put 'True' into their corresponding variables,
-- we can stop waiting.
tmvars <- mapM (\_ -> do -- This argument gives us the number of the
-- worker we are creating, but, hey, who cares!
-- Here's our flag for the worker to raise.
tmvar <- atomically $ newEmptyTMVar
-- Spawn the worker now.
forkIO $ worker chan tmvar
-- Store the flag.
return tmvar
) [1..size]
-- Now, the workers are working. We are going to wait on the
-- corresponding flags until they are finished.
--
-- 'mapM_' is like 'mapM', but it discards the return values. We
-- use it when we are only interested in the side effect: in this
-- case, the waiting ('readTVar' blocks until the variable is not
-- empty).
mapM_ (\tmvar -> atomically $ takeTMVar tmvar) tmvars
where worker chan finished = do
-- The code is pretty self-explanatory: try reading the
-- channel atomically.
maybeTask <- atomically $ tryReadTChan chan
case maybeTask of
-- There's still work to do; so process it and try getting
-- some.
Just task -> do
process task
-- This is a tail recursive call: it's the very last
-- thing that happens in this function, so it actually
-- works pretty much like a while and not like an
-- infinite recursion.
worker chan finished
-- No more work, so this thread may stop ...
Nothing -> do
-- ... but tell the manager we're done before that.
atomically $ putTMVar finished True
-- | Test 'theadPool'.
--
-- Load this file in GHCi and type 'testThreadPool' to see what
-- happens :-)
testThreadPool :: IO ()
testThreadPool = do
-- Will result in ["string1", "string2", ... , "string20"]
--
-- We use '(.)' to apply a series of transformations. First, we
-- transform the number into a string. Second, we prepend "string"
-- to this string. Remember, ("string" ++ ) is a function
-- prepending the word "string" to whatever we give it. Thus
-- ("string" ++) "1" == "string1".
let strings = map (("string" ++) . show) [1..20]
-- Create a channel.
chan <- newTChanIO
-- Put all the strings in the channel.
atomically $ do
-- It's all about partially applied functions again. 'writeTChan'
-- is a function taking two arguments: the channel and the value
-- to write. 'writeTChan chan' is a function taking _one_
-- argument: the value to write to 'chan'.
--
-- We could also write:
--
-- mapM_ (\v -> writeTChan chan v) strings
--
mapM_ (writeTChan chan) strings
-- Create a thread pool of 3 threads, each calling 'putStrLn' on
-- every string from 'chan.
threadPool 3 chan putStrLn
-- Make sure we actually wait for the threads to finish.
putStrLn "All threads finished!"
-- | Parses a string of the form "address[port1-port2]".
--
-- 'break' looks up the first symbol in the string for which the given
-- function returns true, and returns the two halves of the list.
--
-- break odd [2,4,3,5] == ([2,4],[3,5])
--
parseAddress :: String -> (HostName, PortNumber, PortNumber)
parseAddress str = let (host, sPorts) = break (== '[') str
-- sPorts has the form "[number-number]". Drop
-- the brackets.
portsTrimmed = tail $ init sPorts
-- Split on '-', and, in the second half, drop
-- the dash immediately.
(sPort1,(_:sPort2)) = break (== '-') portsTrimmed
in (host, read sPort1, read sPort2)
main :: IO ()
main = do
-- We will now suppose that the list of arguments has the right
-- form. If something goes wrong, we are screwed :-)
-- This lets us get the first two elements of the list.
(sTimeout:sNthreads:addresses) <- getArgs
let timeout = read sTimeout :: Int
nthreads = read sNthreads :: Int
-- Create our channel for tasks.
chan <- newTChanIO
-- We will parse the addresses one by one, generate the
-- corresponding targets, and put them into the channel.
mapM_ (\addr -> do
-- Parse this address.
let (host, port1, port2) = parseAddress addr
-- Now, iterate through the ports in the range
-- 'port1'--'port2' and put the task containing the host
-- name and the port into the channel.
mapM_ (\port ->
atomically $ writeTChan chan (ScanTarget host port)
) [port1..port2]
) addresses
-- Go ahead and scan all our targets in the thread pool of the given
-- size.
threadPool nthreads chan $
(\(ScanTarget host port) -> do
-- So, let's scan this target with the given timeout.
outcome <- testPort host port timeout
-- Now show something.
putStrLn $ host ++ ":" ++ show port ++ " -- " ++ show outcome
)

View file

@ -0,0 +1,169 @@
{- sorting.hs
This file contains the examples on monads to be shown in the fourth
class of the course "Haskell for Life", by Sergiu Ivanov
(sivanov@lacl.fr):
http://lacl.fr/~sivanov/doku.php?id=en:haskell_for_life
This file is meant to be read sequentially.
This file is distributed under the Creative Commons Attribution Alone
licence.-}
import Control.Parallel (par, pseq)
import Control.DeepSeq (force, NFData, deepseq)
import System.Random (getStdGen, randoms)
import System.Environment (getArgs)
import System.Clock
-- | This is an implementation of Quicksort.
--
-- We always choose the head of the list as the pivot, then we put the
-- elements smaller than the pivot in 'lesser', and those greater than
-- the pivot in 'greater'. We then sort the two lists and put them
-- together with the pivot in between.
--
-- The 'Ord' typeclass requires comparison operators (like >=) to be
-- defined for the type.
qsort :: Ord a => [a] -> [a]
qsort [] = []
qsort (pivot:rest) = lesser ++ pivot:greater
where lesser = qsort [x | x <- rest, x < pivot]
greater = qsort [y | y <- rest, y >= pivot]
-- | This is a parallel implementation of Quicksort.
--
-- We would like to have 'lesser' and 'greater' evaluated in parallel.
-- To assure that, we may feel tempted to say
--
-- lesser `par` (lesser ++ pivot:greater)
--
-- In this case the runtime will get the information that we would
-- like 'lesser' evaluated in parallel, ... and then the main thread
-- will start evaluating the arguments to '(++)', sometimes starting
-- with the very same 'lesser'! This means parallel evaluation of
-- 'lesser' will only be initiated if we get lucky and the runtime
-- starts evaluating the second argument of '(++)' before the first
-- one. In order to give 'lesser' some time to get evaluated, we
-- shall therefore also require that 'greater' be evaluated before
-- list concatenation is done, like this:
--
-- lesser `par` ( greater `pseq` (lesser ++ pivot:greater) )
--
-- Moreover, since both `par` and `pseq` are right-associative, we can
-- drop some of the parentheses:
--
-- lesser `par` greater `pseq` (lesser ++ pivot:greater)
--
-- Now, even though everything seems nice and shiny, there's still a
-- big problem: 'par' and 'pseq' only assure some very "shallow"
-- evaluation of their left argument. In the case of our sorting
-- routine this essentially means that only the first elements of
-- 'lesser' and 'greater' are actually evaluated, in parallel, while
-- the rest of the lists are evaluated when the sorted result is
-- _actually_ needed (e.g. when we want to print it); and that
-- evaluation is done sequentially. Therefore, despite our clever
-- usage of 'par' and 'pseq', the greater part of the sorting is done
-- quite sequentially.
--
-- To solve this problem, we should force deep (complete) evaluation
-- of 'lesser' and 'greater' using the aptly named 'force' function:
--
-- force lesser `par` force greater `pseq` (lesser ++ pivot:greater)
--
-- In this case we will really get our sorting executed in parallel.
--
-- We need to use the function 'force' on lists of type 'a', and thus
-- on values of type 'a' as well. The existence of the function
-- 'force' is expressed by the typeclass 'NFData'.
parQsort :: (Ord a, NFData a) => [a] -> [a]
parQsort [] = []
parQsort (pivot:rest) = force lesser
`par` force greater
`pseq` (lesser ++ pivot:greater)
where lesser = parQsort [x | x <- rest, x < pivot]
greater = parQsort [y | y <- rest, y >= pivot]
-- | Now, just for fun, let's consider the number of times we use
-- 'par': we apply it in every node of the recursion tree in
-- 'parQsort', and there more nodes in this recursion tree than there
-- elements in the array to sort! That's actually a problem, since
-- 'par' is not exactly free, even though it's not really costly
-- either.
--
-- To avoid allocating separate jobs for very small lists, we will
-- only use `par` for sorting lists longer than a certain threshold.
--
-- The symbol '@' allows us to refer to the deconstructed list. Thus,
-- when we pattern match on an argument using 'list@(x:xs)' instead of
-- just '(x:xs)', we can use 'list' to refer to the original (not
-- deconstructed) value of the parameter (i.e. to 'list = (x:xs)').
parnQsort :: (Ord a, NFData a) => Int -> [a] -> [a]
parnQsort _ [] = []
parnQsort d list@(pivot:rest) | length list >= d = lesser
`par` greater
`pseq` (lesser ++ pivot:greater)
| otherwise = lesser ++ pivot:greater
where lesser = parnQsort d [x | x <- rest, x < pivot]
greater = parnQsort d [y | y <- rest, y >= pivot]
-- | Produces a sequence of random integers of the given length.
--
-- Pseudo-random numbers are usually generating from some information
-- from the outer world, that is why we have to work within the IO
-- monad.
randomInts :: Int -> IO [Int]
randomInts n = do
gen <- getStdGen -- Get the global random number generator.
let ints = randoms gen -- Get an infinite list of random 'Int's.
return $ take n ints -- Return the first 'n' random values.
-- | Runs an IO action and then outputs the running time.
--
-- Let's look at the type signature: according to its return type,
-- this function is in the IO monad, and it returns a 'TimeSpec', a
-- type defined in 'Data.Clock' that contains two fields: one for
-- seconds and another one for nanoseconds. The only argument of this
-- function is an _unevaluated_ value (a thunk) which the function
-- will evaluate and measure the time it took. The evaluation will be
-- forced via the 'deepseq' function, and the existence of this
-- function for the type 'a' is guaranteed by requiring that it should
-- belong to the 'NFData' typeclass.
clockedRun :: NFData a => a -> IO TimeSpec
clockedRun x = do
-- We get the time from the monotonic clock: its value can never be
-- changed, so we do not risk getting biased results.
start <- getTime Monotonic
end <- x `deepseq` getTime Monotonic
return (diffTimeSpec start end)
-- | Let's test all three functions.
main :: IO ()
main = do
args <- getArgs -- Get the command line arguments.
-- If there are some arguments, treat the first one as the length of
-- the list; otherwise, use the default length: 100000 with many zeros.
let n | null args = 100000
| otherwise = read (head args)
-- Generate the random list.
putStrLn $ "Generating " ++ show n ++ " random integers."
list <- randomInts n
-- Run the sequential Quicksort and measure the time.
seqTime <- clockedRun (qsort list)
putStrLn $ "Sequential Quicksort worked in " ++ show (nsec seqTime) ++ " ns."
-- Run the first version of parallel Quicksort and measure the time.
parTime <- clockedRun (parQsort list)
putStrLn $ "Parallel Quicksort (abusive variant) worked in " ++ show (nsec parTime) ++ " ns."
-- Run the cleverer version of parallel Quicksort and measure the
-- time. Don't use 'par' for lists having less than 1000 elements.
parnTime <- clockedRun (parnQsort 1000 list)
putStrLn $ "Parallel Quicksort (cleverer variant) worked in " ++ show (nsec parnTime) ++ " ns."

View file

@ -0,0 +1,40 @@
{- typeclasses.hs
This file contains some of the examples on typeclasses to be shown in
the second class of the course "Haskell for Life", by Sergiu Ivanov
(sivanov@lacl.fr):
http://lacl.fr/~sivanov/doku.php?id=en:haskell_for_life
This file is distributed under the Creative Commons Attribution Alone
licence.-}
-- | A simple redefinition of the 'Eq' typeclass.
class BasicEq a where
isEqual :: a -> a -> Bool
isElement :: BasicEq a => a -> [a] -> Bool
isElement _ [] = False
isElement y (x:xs) | isEqual y x = True
| otherwise = isElement y xs
instance BasicEq Bool where
isEqual True True = True
isEqual False False = True
isEqual _ _ = False
instance BasicEq a => BasicEq [a] where
isEqual [] [] = True
isEqual _ [] = False
isEqual [] _ = False
isEqual (x:xs) (y:ys) | isEqual x y = isEqual xs ys
| otherwise = False
-- | This typeclasses are simpler than 'Show' and 'Read', but
-- essentially capture their principal function.
class SimplifiedShow a where
show :: a -> String
class SimplifiedRead a where
read :: String -> a

View file

@ -0,0 +1,61 @@
{- types.hs
This file contains some of the examples on types to be shown in the
second class of the course "Haskell for Life", by Sergiu Ivanov
(sivanov@lacl.fr):
http://lacl.fr/~sivanov/doku.php?id=en:haskell_for_life
This file is distributed under the Creative Commons Attribution Alone
licence.-}
data BookInfo = Book Int String [String]
deriving (Show)
data Coordinates = Cartesian2D Double Double | Polar2D Double Double
deriving (Show)
printCoordinate :: Coordinates -> String
printCoordinate (Cartesian2D x y) = "x=" ++ show x ++ ",y=" ++ show y
printCoordinate (Polar2D ρ φ) = "ρ=" ++ show ρ ++ ",φ=" ++ show φ
data Pair a = Pair a a
deriving (Show)
-- | Returns 'True' if the given 'Maybe' contains 'Nothing'.
myIsNothing :: Maybe a -> Bool
myIsNothing Nothing = True
myIsNothing (Just _) = False
-- | Extracts the contents of a 'Maybe' (unpacks it).
myFromJust :: Maybe a -> a
myFromJust (Just x) = x
data List a = Cons a (List a) | Nil
deriving (Show)
-- | Converts an instance of 'List' to a normal Haskell list.
fromMyList :: List a -> [a]
fromMyList (Cons x xs) = x:fromMyList xs
fromMyList Nil = []
-- | Converts a normal Haskell list to an instance of 'List'.
toMyList :: [a] -> List a
toMyList (x:xs) = Cons x (toMyList xs)
toMyList [] = Nil
data FunnyList a = a :/ (FunnyList a) | Void
deriving Show
data Tree a = Node a (Tree a) (Tree a) | Empty
deriving (Show, Read)
-- | Returns a list of the values contained in the leaves of a tree.
fruit :: Tree a -> [a]
fruit Empty = []
fruit (Node x Empty Empty) = [x]
fruit (Node _ l r) = fruit l ++ fruit r

Binary file not shown.

104
en/h4life.org Normal file
View file

@ -0,0 +1,104 @@
#+TITLE: Haskell for Life
#+LANGUAGE: en
#+ATTR_HTML: :alt in French :class lang-lifted
[[file:../fr/h4life.org][file:../content/imgs/fr.png]]
#+ATTR_HTML: :alt return home :class home
[[file:home.org][file:../content/imgs/home.png]]
The goal of this short course is to familiarise the students with the
computational model behind a mainstream high-abstraction functional
programming language, with focus on developing practical skills. After
having taken this course, the students will be able to solve practical
programming problems of medium complexity by writing idiomatic Haskell
code and making use of the rich library ecosystem.
The course is intended for a wide audience and only requires basic
programming skills in one of the other mainstream languages (C/C++,
Java, C#/.NET, Python, etc.).
The course relies heavily on showing actual interactive Haskell
sessions and should be directly driven by feedback from the students.
The course consists of 4 parts, the first 3 of which introduce some
essential concepts, and the last one focuses on practical examples of
parallel and concurrent programming. The parts of the course may be
taught over multiple sessions of about 2 hours; in particular, the
first part should most probably be spread over two sessions to help
students digest the basic concepts. At the end of the course the
students may be required to submit a course project. The students who
submit a working course project may be awarded 2 ECTS.
The course is somewhat loosely based on the awesome book [[http://book.realworldhaskell.org/][Real World
Haskell]].
#+ATTR_HTML: :alt image of Creative Commons Attribution Alone licence :class ccby
[[https://en.wikipedia.org/wiki/Creative_Commons_license][file:../content/imgs/ccby.png]]
The materials of this course are distributed under the [[https://en.wikipedia.org/wiki/Creative_Commons_license][Creative
Commons Attribution Alone licence]].
* Introduction
The fundamental ideas behind functional programming are introduced,
with particular focus on handling functions as first-class values
and on higher order functions. Strict and static typing discipline
in Haskell is discussed and compared to typing disciplines in other
mainstream programming languages. Basic syntax is described along
with the explanations, including the syntax for definitions of
simple data types.
The slides of this part are available [[file:../content/courses/h4life/h4life-01.pdf][here]]. [[file:../content/courses/h4life/h4life-funcs.hs][This file]] contains the
examples.
* Typeclasses
Typeclasses in Haskell are introduced and compared to classes and
types in other mainstream programming languages. Subtle differences
are pointed out and discussed at length to avoid confusion.
The slides of this part are available [[file:../content/courses/h4life/h4life-02.pdf][here]]. [[file:../content/courses/h4life/h4life-types.hs][This]] and [[file:../content/courses/h4life/h4life-typeclasses.hs][this file]]
contain the examples. The former file contains Unicode characters,
so be sure to choose the correct Unicode encoding (UTF-8) to view it
properly.
* Monads
Monads as a way to encapsulate repetitive details of the computation
are introduced. The =Maybe= and =List= monads are described. An
overview of the monads =Reader=, =Writer=, and =State= is provided.
The slides of this part are available [[file:../content/courses/h4life/h4life-03.pdf][here]]. [[file:../content/courses/h4life/h4life-monads.hs][This file]] contains the
examples.
* Parallel and Concurrent Programming
Techniques of parallel and concurrent programming in Haskell are
described. Sparks as building blocks for a parallel program are
discussed. Spawning threads and synchronising them via thread-safe
shared storage is covered. A basic port scanner is presented as a
practical example.
The slides of this part are available [[file:../content/courses/h4life/h4life-04.pdf][here]]. The sorting example can
be found [[file:../content/courses/h4life/h4life-sorting.hs][here]]. The source code of the port scanner is available
[[file:../content/courses/h4life/h4life-scanner.hs][here]].
For further details on parallel and concurrent programming in
Haskell, check out [[file:../content/courses/h4life/marlow-tutorial.pdf][Simon Marlow's tutorial]].
* Teaching Sites
I taught this course at the [[http://www.tucs.fi/][Turku Center for Computer Science]]
(January 2016), at [[https://www.pentalog.com/locations/chisinau-nearshore-delivery-center/][Pentalog Chișinău]] (December 2016), and at the
[[http://fcim.utm.md/][Faculty of Computer Science of the Technical University of Moldova]]
(December 2016).
The exam subject for the version of the course I taught at the Turku
Center of Computer Science can be found [[file:../content/courses/h4life/h4life-exam.pdf][here]].
* Local Variables :noexport:
# Local Variables:
# org-link-file-path-type: relative
# eval: (auto-fill-mode)
# ispell-local-dictionary: "en"
# End:

View file

@ -66,8 +66,8 @@ Jump to:
- [[file:alife-intro.org][a very quick introduction to artificial life]]
- [[file:pn-biomodelling.org][Petri nets for biomodelling]]
- [[file:cytoscape-intro.org][introduction to Cytoscape]]
- Haskell for Life
- [[file:togit.org][To Git or Not to Git]]
- [[file:h4life.org][Haskell for Life]]
- operating systems and networks
- basic parsing

116
fr/h4life.org Normal file
View file

@ -0,0 +1,116 @@
#+TITLE: Haskell for Life
#+LANGUAGE: fr
#+ATTR_HTML: :alt en anglais :class lang-lifted
[[file:../en/h4life.org][file:../content/imgs/en.png]]
#+ATTR_HTML: :alt return home :class home
[[file:home.org][file:../content/imgs/home.png]]
Le but de ce cours de courte durée est de familiariser les étudiants
avec le modèle de calcul derrière un langage fonctionnel de haut
niveau et grand public. Le cours se focalise plutôt sur l'acquisition
de connaissances pratiques. Grâce à ce cours, les étudiants seront
capables de résoudre des problèmes pratiques de programmation de
complexité moyenne en écrivant du code Haskell idiomatique et en
utilisant l'écosystème riche de librairies propre à ce langage.
Ce cours vise un public large, le seul prérequis étant une expérience
de programmation superficielle dans un des langages populaires :
(C/C++, Java, C#/.NET, Python, etc.).
L'activité essentielle pour l'enseignement de ce cours est la
démonstration de sessions interactives de Haskell. Le processus
d'enseignement sera donc directement dirigé par le retour de la part
des étudiants.
Le cours consiste en 4 parties, dont les 3 premières définissent
quelques notions essentielles, alors que la dernière fournit des
exemples de programmation parallèle et concurrente. Une partie peut
être enseignée sur plusieurs séances de 2 heures ; c'est notamment le
cas de la première partie qui devrait probablement être traitée en
deux temps pour faciliter l'apprentissage. À la fin du cours les
étudiants peuvent soumettre un projet ; les étudiants ayant réussi à
soumettre un projet peuvent se voir accorder 2 ECTS.
Les dispositifs de ce cours ainsi que les explications dans le code
source sont en anglais.
Ce cours suit approximativement le merveilleux livre [[http://book.realworldhaskell.org/][Real World
Haskell]].
#+ATTR_HTML: :alt image de la licence Creative Commons Attribution Alone :class ccby
[[https://fr.wikipedia.org/wiki/Licence_Creative_Commons][file:../content/imgs/ccby.png]]
Les matériaux de ce cours sont distribués sous la [[https://fr.wikipedia.org/wiki/Licence_Creative_Commons][licence Creative
Commons Paternité]].
* Introduction
Cette partie introduit les idées fondamentales derrière la
programmation fonctionnelle ; il s'agit des fonctions de première
classe et des fonctions d'ordre supérieur. Le typage strict et
statique en Haskell est présenté et comparé aux autres stratégies de
typage que l'on retrouve dans d'autres langages de
programmation. Quelques éléments essentiels de la syntaxe sont
expliqués, y compris la syntaxe pour la définition de types de
données simples.
Les diapositives de cette partie se trouvent [[file:../content/courses/h4life/h4life-01.pdf][ici]]. Les exemples
présentés sont [[file:../content/courses/h4life/h4life-funcs.hs][ici]].
* Classes de types
Cette partie définit les classes de types en Haskell et les compare
aux classes et aux types dans d'autres langages de
programmation. Quelques différences subtiles sont mises en avant
pour éviter toute confusion.
Les diapositives de cette partie se trouvent [[file:../content/courses/h4life/h4life-02.pdf][ici]]. Les exemples
présentés se trouvent [[file:../content/courses/h4life/h4life-types.hs][ici]] et encore [[file:../content/courses/h4life/h4life-typeclasses.hs][ici]]. Le premier de ces fichiers
contient des caractères Unicode ; il faut donc bien choisir
l'encodage de l'affichage (Unicode ou UTF-8).
* Monades
Dans cette partie les monades sont présentées comme des outils pour
l'élimination des aspects répétitifs d'un calcul. Dans un premier
temps, les monades =Maybe= et =List= sont décrites. Ensuite, un
aperçu des monades =Reader=, =Writer=, et =State= est proposé.
Les diapositives de cette partie se trouvent [[file:../content/courses/h4life/h4life-03.pdf][ici]]. Les exemples
présentés se trouvent [[file:../content/courses/h4life/h4life-monads.hs][ici]].
* Parallel and Concurrent Programming
Cette partie décrit quelques techniques de base pour la
programmation parallèle et concurrente en Haskell. Dans un premier
temps, les annotations pour la programmation parallèle sont
présentées. Ensuite, une façon de lancer des fils d'exécution
parallèle est décrite, ainsi que les stratégies de synchronisation
par des variables partagées en exclusion mutuelle. Un scanner de
ports rudimentaire est développé pour une démonstration pratique de
ces techniques.
Les diapositives de cette partie se trouvent [[file:../content/courses/h4life/h4life-04.pdf][ici]]. L'implémentation
du tri rapide présentée dans le cours est [[file:../content/courses/h4life/h4life-sorting.hs][ici]]. Le code du scanner de
ports se trouve [[file:../content/courses/h4life/h4life-scanner.hs][ici]].
[[file:../content/courses/h4life/marlow-tutorial.pdf][Ce tutoriel]] (en anglais) sur la programmation parallèle et
concurrente en Haskell par Simon Marlow donne (beaucoup) plus de
détails.
* Sites d'enseignements
J'ai enseigné ce cours à [[http://www.tucs.fi/][Turku Center for Computer Science]] (janvier
2016), à [[https://www.pentalog.com/locations/chisinau-nearshore-delivery-center/][Pentalog Chișinău]] (décembre 2016), ainsi qu'à la [[http://fcim.utm.md/][faculté
d'informatique de l'Université technique de Moldavie]] (décembre
2016).
La consigne de l'examen pour la version du cours que j'ai enseignée
à Turku Center for Computer Science se trouve [[file:../content/courses/h4life/h4life-exam.pdf][ici]].
* Local Variables :noexport:
# Local Variables:
# org-link-file-path-type: relative
# eval: (auto-fill-mode)
# ispell-local-dictionary: "fr"
# End:

View file

@ -70,7 +70,7 @@ Liens rapides :
- [[file:pn-biomodelling.org][réseaux de Petri pour la biomodélisation]]
- [[file:cytoscape-intro.org][introduction à Cytoscape]]
- [[file:togit.org][To Git or Not to Git]]
- Haskell for Life
- [[file:h4life.org][Haskell for Life]]
- introduction au systèmes d'exploitation et réseaux
- bases de l'analyse syntaxique