progress
This commit is contained in:
parent
cc7ee03907
commit
250335f16b
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
|
|
11
src/posts/0010-Haskell-Now/fib_lazy_trace.hs
Normal file
11
src/posts/0010-Haskell-Now/fib_lazy_trace.hs
Normal 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)
|
|
@ -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
|
||||
|
|
19
src/posts/0010-Haskell-Now/io_sum_ask.hs
Normal file
19
src/posts/0010-Haskell-Now/io_sum_ask.hs
Normal 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
|
|
@ -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."
|
||||
|
|
|
@ -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 |
Loading…
Reference in a new issue