This commit is contained in:
Yann Esposito (Yogsototh) 2019-12-26 12:45:11 +01:00
parent cc7ee03907
commit 250335f16b
Signed by untrusted user who does not match committer: yogsototh
GPG key ID: 7B19A4C650D59646
10 changed files with 682 additions and 594 deletions

View file

@ -2,13 +2,19 @@
Author: Yann Esposito
*/
/* Fonts */
:root {
--lh: 16px;
}
body {
font: 14px/1.4 monospace;
line-height: 16px;
line-height: var(--lh);
}
pre, pre code {
line-height: 1em;
}
blockquote pre {
line-height: var(--lh);
}
/* Layout */
body, h1, h2, h3, h4, h5, h6, pre, code, blockquote, ol, ul, ol ol, ul ul, ul ol, ol
ul, li, p, section, header, footer, img {
@ -20,8 +26,8 @@ ul, li, p, section, header, footer, img {
}
h1, h2, h3, h4, h5, h6, pre, code, blockquote, p, ul, ol, section, header,
figure,table {
margin-top: 1em;
margin-bottom: 1em;
margin-top: var(--lh);
margin-bottom: var(--lh);
}
li {
display: block;
@ -122,7 +128,7 @@ hr {
margin:0;
}
#table-of-contents {
margin-bottom: 1em;
margin-bottom: var(--lh);
}
#postamble:before, hr:after {
text-align: center;
@ -186,7 +192,7 @@ figure, .figure {
margin-top: 0;
}
#postamble {
margin-top: 1em;
margin-top: var(--lh);
}
.timestamp-wrapper {
font-size: 12px;
@ -252,9 +258,9 @@ figure, .figure {
@media (prefers-color-scheme: light) {
:root {
--bg: var(--b3);
--fg: var(--b00);
--fg: var(--b01);
--bg2: var(--b2);
--fg2: var(--b1);
--fg2: var(--b00);
--rfg: var(--b01);
--rbg: var(--b2);
--bdr: var(--b2);

View file

@ -1,4 +1,4 @@
fib :: [Integer]
fib = 1:1:zipWith (+) fib (tail fib)
main = traverse_ print (take 20 (drop 200 fib))
main = traverse print (take 20 (drop 200 fib))

View file

@ -0,0 +1,11 @@
import Debug.Trace
-- like + but each time this is evaluated print a trace
tracedPlus x y = trace ("> " ++ show x ++ " + " ++ show y) (x + y)
fib :: [Integer]
fib = 1:1:zipWith tracedPlus fib (tail fib)
main = do
print (fib !! 10)
print (fib !! 12)

View file

@ -6,6 +6,7 @@
#+keywords: Haskell, programming, functional, tutorial |
#+DESCRIPTION: A very dense introduction and Haskell tutorial. Brace yourself.
#+OPTIONS: auto-id:t toc:t
#+STARTUP: overview
#+begin_notes
A very short and intense introduction to Haskell.
@ -529,7 +530,7 @@ For example, in =C=, you'll have to declare a function for =int=, for
But, what type should we declare?
To discover the type Haskell has found for us, just launch ghci:
#+BEGIN_SRC
#+BEGIN_EXAMPLE
% ghci
GHCi, version 7.0.4: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
@ -539,13 +540,13 @@ To discover the type Haskell has found for us, just launch ghci:
Prelude> let f x y = x*x + y*y
Prelude> :type f
f :: Num a => a -> a -> a
#+END_SRC
#+END_EXAMPLE
Uh? What is this strange type?
#+BEGIN_EXAMPLE
#+BEGIN_SRC haskell
Num a => a -> a -> a
#+END_EXAMPLE
#+END_SRC
First, let's focus on the right part =a -> a -> a=.
To understand it, just look at a list of progressive examples:
@ -1912,23 +1913,18 @@ Also, note in Haskell there is a notation for infinite lists
and most functions will work with them. Also, there is a built-in
function =take= which is equivalent to our =take'=.
This code is mostly the same as the previous one.
*** Infinite Trees
:PROPERTIES:
:CUSTOM_ID: infinite-trees
:END:
#+begin_src haskell :tangle infinite_tree.hs :exports none
import Data.Tree (Tree,Forest(..))
import qualified Data.Tree as Tree
#+end_src
#+BEGIN_SRC haskell :tangle infinite_tree.hs
data BinTree a = Empty
| Node a (BinTree a) (BinTree a)
deriving (Eq,Ord,Show)
#+END_SRC
#+begin_src haskell :tangle infinite_tree.hs :exports none
-- | Function to transform our internal BinTree type to the
-- type of Tree declared in Data.Tree (from containers package)
@ -1941,7 +1937,6 @@ This code is mostly the same as the previous one.
-- | Function that given a BinTree print a representation of it in the console
prettyPrintTree :: (Show a) => BinTree a -> IO ()
prettyPrintTree = putStrLn . Tree.drawForest . binTreeToForestString
#+end_src
@ -2119,7 +2114,11 @@ Look at the result for
`- 3
#+END_EXAMPLE
#+begin_notes
*** Fibonnacci infinite list
:PROPERTIES:
:CUSTOM_ID: fibonnacci-infinite-list
:END:
The important things to remember.
Haskell handle infinite structures naturally mostly because it is not strict.
@ -2130,19 +2129,17 @@ like this common example:
fib :: [Integer]
fib = 1:1:zipWith (+) fib (tail fib)
main = traverse_ print (take 20 (drop 200 fib))
main = traverse print (take 20 (drop 200 fib))
#+end_src
Many new details in this small code. Don't worry if you do not get all details:
- =fib= is a list of Integer, not a function
- =(!!)= is used to get the nth element of a list.
- =drop n= remove n element of a list
- =take n= keep the first n elements of a list
- =zipWith op [a1,a2,a3,...] [b1,b2,b3,...]= will generate the list
=[op a1 b1,op a2 b2,op a3 b3, .... ]=
- =traverse_= is like map but for performing effects (in this case print)
and discarding any result if any.
- =traverse= is like map but for performing effects (in this case print)
This progam print all fibonnacci numbers from 201 to 221 instantaneously.
Because, =fib= is a list that will be used as "cache" to compute each
@ -2176,7 +2173,49 @@ user 0m0.192s
sys 0m0.058s
#+end_example
#+end_notes
Let's see how this work using =Debug.Trace=:
{{{lnk(fib_lazy_trace.hs)}}}
#+begin_src haskell :tangle fib_lazy_trace.hs
import Debug.Trace
-- like + but each time this is evaluated print a trace
tracedPlus x y = trace ("> " ++ show x ++ " + " ++ show y) (x + y)
fib :: [Integer]
fib = 1:1:zipWith tracedPlus fib (tail fib)
main = do
print (fib !! 10)
print (fib !! 12)
#+end_src
#+begin_example
[hs:hsenv]> runghc fib_lazy_trace.hs
> 1 + 1
> 1 + 2
> 2 + 3
> 3 + 5
> 5 + 8
> 8 + 13
> 13 + 21
> 21 + 34
> 34 + 55
89
> 55 + 89
> 89 + 144
233
#+end_example
Notice how, once computed, the list is kept in memory.
This is why when the second time we ask for the 12th element of fib we only
perform two more additions.
This is both a blessing and a curse.
A blessing if you know when to use this as in this example.
And a curse as if do not take care about lazyness it will come back at you
with memory leaks.
After a bit of experience, most Haskellers can avoid memory leaks naturally.
* Difficulty: Hard
:PROPERTIES:
@ -2334,6 +2373,7 @@ This is a very common type in Haskell.
#+BEGIN_SRC haskell :tangle io_sum_safe.hs
import Data.Maybe
import Text.Read (readMaybe)
#+END_SRC
What is this thing?
@ -2346,19 +2386,10 @@ Its definition is:
This is a nice way to tell there was an error while trying to
create/compute a value.
The =maybeRead= function is a great example of this.
The =readMaybe= function is a great example of this.
This is a function similar to the function =read=[fn:4], but if something
goes wrong the returned value is =Nothing=.
If the value is right, it returns =Just <the value>=.
Don't try to understand too much of this function.
I use a lower level function than =read=: =reads=.
#+BEGIN_SRC haskell :tangle io_sum_safe.hs
maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
[(x,"")] -> Just x
_ -> Nothing
#+END_SRC
Now to be a bit more readable, we define a function which goes like this:
If the string has the wrong format, it will return =Nothing=.
@ -2366,7 +2397,7 @@ Otherwise, for example for "1,2,3", it will return =Just [1,2,3]=.
#+BEGIN_SRC haskell :tangle io_sum_safe.hs
getListFromString :: String -> Maybe [Integer]
getListFromString str = maybeRead $ "[" ++ str ++ "]"
getListFromString str = readMaybe $ "[" ++ str ++ "]"
#+END_SRC
We simply have to test the value in our main function.
@ -2376,7 +2407,7 @@ We simply have to test the value in our main function.
main = do
putStrLn "Enter a list of numbers (separated by comma):"
input <- getLine
let maybeList = getListFromString input in
let maybeList = getListFromString input
case maybeList of
Just l -> print (sum l)
Nothing -> putStrLn "Bad format. Good Bye."
@ -2411,26 +2442,23 @@ enters a valid answer.
We keep the first part:
#+BEGIN_SRC haskell
#+BEGIN_SRC haskell :tangle io_sum_ask.hs
import Data.Maybe
import Text.Read (readMaybe)
maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
[(x,"")] -> Just x
_ -> Nothing
getListFromString :: String -> Maybe [Integer]
getListFromString str = maybeRead $ "[" ++ str ++ "]"
getListFromString str = readMaybe $ "[" ++ str ++ "]"
#+END_SRC
Now we create a function which will ask the user for an list of integers
until the input is right.
#+BEGIN_SRC haskell
#+BEGIN_SRC haskell :tangle io_sum_ask.hs
askUser :: IO [Integer]
askUser = do
putStrLn "Enter a list of numbers (separated by comma):"
input <- getLine
let maybeList = getListFromString input in
let maybeList = getListFromString input
case maybeList of
Just l -> return l
Nothing -> askUser
@ -2442,7 +2470,7 @@ some IO actions.
Some people might explain while waving their hands:
#+BEGIN_QUOTE
«This is an =[Integer]= inside an =IO=»
«This is an =[Integer]= inside an =IO=.»
#+END_QUOTE
If you want to understand the details behind all of this, you'll have to
@ -2452,7 +2480,7 @@ remember to think about the type.
Finally our main function is much simpler:
#+BEGIN_SRC haskell
#+BEGIN_SRC haskell :tangle io_sum_ask.hs
main :: IO ()
main = do
list <- askUser
@ -2489,46 +2517,46 @@ If you practice a bit, you should be able to /use/ =IO=.
[[./magritte_pipe.jpg]]
#+BEGIN_QUOTE
Here is a %tldr for this section.
{{{tldr}}}
To separate pure and impure parts, =main= is defined as a function
which modifies the state of the world.
To separate pure and impure parts, =main= is defined as a function which
modifies the state of the world.
#+BEGIN_EXAMPLE
main :: World -> World
#+END_EXAMPLE
A function is guaranteed to have side effects only if it has this
type. But look at a typical main function:
A function is guaranteed to have side effects only if it has this type.
But look at a typical main function:
#+BEGIN_EXAMPLE
#+BEGIN_SRC haskell
main w0 =
let (v1,w1) = action1 w0 in
let (v2,w2) = action2 v1 w1 in
let (v3,w3) = action3 v2 w2 in
action4 v3 w3
#+END_EXAMPLE
#+END_SRC
We have a lot of temporary elements (here =w1=, =w2= and =w3=) which
must be passed on to the next action.
We have a lot of temporary elements (here =w1=, =w2= and =w3=) which must
be passed on to the next action.
We create a function =bind= or =(>>=)=. With =bind= we don't need
temporary names anymore.
We create a function =bind= or ~(>>=)~.
With =bind= we don't need temporary names anymore.
#+BEGIN_EXAMPLE
#+BEGIN_SRC haskell
main =
action1 >>= action2 >>= action3 >>= action4
#+END_EXAMPLE
#+END_SRC
Bonus: Haskell has syntactical sugar for us:
#+BEGIN_EXAMPLE
#+BEGIN_SRC haskell
main = do
v1 <- action1
v2 <- action2 v1
v3 <- action3 v2
action4 v3
#+END_EXAMPLE
#+END_SRC
#+END_QUOTE
Why did we use this strange syntax, and what exactly is this =IO= type?
@ -2542,7 +2570,7 @@ focus on the impure parts:
askUser = do
putStrLn "Enter a list of numbers (separated by commas):"
input <- getLine
let maybeList = getListFromString input in
let maybeList = getListFromString input
case maybeList of
Just l -> return l
Nothing -> askUser
@ -2712,7 +2740,7 @@ For example, we could also have:
With, of course: =actionN w :: (World) -> (a,World)=.
#+BEGIN_QUOTE
IMPORTANT: there are only two important patterns to consider:
*IMPORTANT*: there are only two important patterns to consider:
#+BEGIN_SRC haskell
let (x,w1) = action1 w0 in
@ -2727,8 +2755,8 @@ With, of course: =actionN w :: (World) -> (a,World)=.
#+END_SRC
#+END_QUOTE
#+CAPTION: Jocker pencil trick
[[./jocker_pencil_trick.jpg]]
#+CAPTION: Slave Market with the disappearing bust of Voltaire
[[./slave-market-with-the-disappearing-bust-of-voltaire.jpg]]
Now, we will do a magic trick.
We will make the temporary world symbols "disappear".
@ -3612,10 +3640,37 @@ Thank you man.
shouldn't see such use in a real application except maybe for
debugging purposes.
[fn:6] For the curious ones, the real type is
[fn:6] For the curious ones, the real type looks like
=data IO a = IO {unIO :: State# RealWorld -> (# State# RealWorld, a #)}=.
All the =#= has to do with optimisation and I swapped the fields
in my example. But this is the basic idea.
All the =#= has to do with optimisation.
I swapped the fields in my example.
But this is the basic idea.
As of today, the definition of =IO= is no more visible into =base=.
We have the following explanation in [[http://hackage.haskell.org/package/base-4.12.0.0/docs/src/GHC.IO.html][=GHC.IO.hs=]]:
#+begin_quote
#+begin_src
The IO Monad is just an instance of the ST monad, where the state is
the real world. We use the exception mechanism (in GHC.Exception) to
implement IO exceptions.
NOTE: The IO representation is deeply wired in to various parts of the
system. The following list may or may not be exhaustive:
Compiler - types of various primitives in PrimOp.hs
RTS - forceIO (StgStartup.cmm)
- catchzh_fast, (un)?blockAsyncExceptionszh_fast, raisezh_fast
(Exception.cmm)
- raiseAsync (RaiseAsync.c)
Prelude - GHC.IO.hs, and several other places including
GHC.Exception.hs.
Libraries - parts of hslibs/lang.
--SDM
#+end_src
#+end_quote
[fn:7] Well, you'll certainly need to practice a bit to get used to them
and to understand when you can use them and create your own. But

View file

@ -0,0 +1,19 @@
import Data.Maybe
import Text.Read (readMaybe)
getListFromString :: String -> Maybe [Integer]
getListFromString str = readMaybe $ "[" ++ str ++ "]"
askUser :: IO [Integer]
askUser = do
putStrLn "Enter a list of numbers (separated by comma):"
input <- getLine
let maybeList = getListFromString input
case maybeList of
Just l -> return l
Nothing -> askUser
main :: IO ()
main = do
list <- askUser
print $ sum list

View file

@ -1,18 +1,14 @@
import Data.Maybe
maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
[(x,"")] -> Just x
_ -> Nothing
import Text.Read (readMaybe)
getListFromString :: String -> Maybe [Integer]
getListFromString str = maybeRead $ "[" ++ str ++ "]"
getListFromString str = readMaybe $ "[" ++ str ++ "]"
main :: IO ()
main = do
putStrLn "Enter a list of numbers (separated by comma):"
input <- getLine
let maybeList = getListFromString input in
let maybeList = getListFromString input
case maybeList of
Just l -> print (sum l)
Nothing -> putStrLn "Bad format. Good Bye."

View file

@ -6,6 +6,7 @@ let
haskellDeps = ps: with ps; [
base
protolude
containers
];
ghc = haskellPackages.ghcWithPackages haskellDeps;

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB