wip
This commit is contained in:
parent
7784f02483
commit
cc7ee03907
7 changed files with 324 additions and 72 deletions
4
src/posts/0010-Haskell-Now/fib_lazy.hs
Normal file
4
src/posts/0010-Haskell-Now/fib_lazy.hs
Normal file
|
@ -0,0 +1,4 @@
|
|||
fib :: [Integer]
|
||||
fib = 1:1:zipWith (+) fib (tail fib)
|
||||
|
||||
main = traverse_ print (take 20 (drop 200 fib))
|
|
@ -190,6 +190,7 @@ The article contains five parts:
|
|||
haskellDeps = ps: with ps; [
|
||||
base
|
||||
protolude
|
||||
containers
|
||||
];
|
||||
|
||||
ghc = haskellPackages.ghcWithPackages haskellDeps;
|
||||
|
@ -203,6 +204,9 @@ The article contains five parts:
|
|||
pkgs.stdenv.mkDerivation {
|
||||
name = "env";
|
||||
buildInputs = nixPackages;
|
||||
shellHook = ''
|
||||
export PS1="\n[hs:\033[1;32m\]\W\[\033[0m\]]> "
|
||||
'';
|
||||
}
|
||||
#+end_src
|
||||
|
||||
|
@ -1751,7 +1755,8 @@ Node 7 (Node 2 Empty (Node 4 Empty Empty)) (Node 8 Empty Empty)
|
|||
This is an informative but quite unpleasant representation of our tree.
|
||||
|
||||
I've added the =containers= package in the =shell.nix= file, it is time to
|
||||
use this library which contain an helper to show trees.
|
||||
use this library which contain functions to show trees and list of trees
|
||||
(forest) named =drawTree= and =drawForest=.
|
||||
|
||||
#+BEGIN_SRC haskell :tangle pretty_tree.hs
|
||||
import Data.Tree (Tree,Forest(..))
|
||||
|
@ -1814,8 +1819,6 @@ Int binary tree:
|
|||
|
|
||||
`- 23
|
||||
|
||||
|
||||
|
||||
Note we could also use another type
|
||||
|
||||
String binary tree:
|
||||
|
@ -1829,11 +1832,8 @@ String binary tree:
|
|||
|
|
||||
`- "yog"
|
||||
|
||||
|
||||
|
||||
As we can test equality and order trees, we can make tree of trees!
|
||||
|
||||
|
||||
Binary tree of Char binary trees:
|
||||
Node 'f' Empty (Node 'o' Empty Empty)
|
||||
|
|
||||
|
@ -1914,18 +1914,41 @@ function =take= which is equivalent to our =take'=.
|
|||
|
||||
This code is mostly the same as the previous one.
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
import Debug.Trace (trace)
|
||||
import Data.List
|
||||
#+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)
|
||||
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)
|
||||
-- so that the function Tree.drawForest can use
|
||||
binTreeToForestString :: (Show a) => BinTree a -> Forest String
|
||||
binTreeToForestString Empty = []
|
||||
binTreeToForestString (Node x left right) =
|
||||
[Tree.Node (show x) ((binTreeToForestString left) ++ (binTreeToForestString right))]
|
||||
|
||||
-- | 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
|
||||
|
||||
|
||||
Suppose we don't mind having an ordered binary tree.
|
||||
Here is an infinite binary tree:
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
#+BEGIN_SRC haskell :tangle infinite_tree.hs
|
||||
nullTree = Node 0 nullTree nullTree
|
||||
#+END_SRC
|
||||
|
||||
|
@ -1933,7 +1956,7 @@ A complete binary tree where each node is equal to 0.
|
|||
Now I will prove you can manipulate this object using the following
|
||||
function:
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
#+BEGIN_SRC haskell :tangle infinite_tree.hs
|
||||
-- take all element of a BinTree
|
||||
-- up to some depth
|
||||
treeTakeDepth _ Empty = Empty
|
||||
|
@ -1947,34 +1970,83 @@ function:
|
|||
|
||||
See what occurs for this program:
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
main = print $ treeTakeDepth 4 nullTree
|
||||
{{{lnk(infinite_tree.hs)}}}
|
||||
#+BEGIN_SRC haskell :tangle infinite_tree.hs
|
||||
main = prettyPrintTree (treeTakeDepth 4 nullTree)
|
||||
#+END_SRC
|
||||
|
||||
This code compiles, runs and stops giving the following result:
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
< 0
|
||||
: |-- 0
|
||||
: | |-- 0
|
||||
: | | |-- 0
|
||||
: | | `-- 0
|
||||
: | `-- 0
|
||||
: | |-- 0
|
||||
: | `-- 0
|
||||
: `-- 0
|
||||
: |-- 0
|
||||
: | |-- 0
|
||||
: | `-- 0
|
||||
: `-- 0
|
||||
: |-- 0
|
||||
: `-- 0
|
||||
[hs:hsenv]> runghc infinite_tree.hs
|
||||
0
|
||||
|
|
||||
+- 0
|
||||
| |
|
||||
| +- 0
|
||||
| | |
|
||||
| | +- 0
|
||||
| | |
|
||||
| | `- 0
|
||||
| |
|
||||
| `- 0
|
||||
| |
|
||||
| +- 0
|
||||
| |
|
||||
| `- 0
|
||||
|
|
||||
`- 0
|
||||
|
|
||||
+- 0
|
||||
| |
|
||||
| +- 0
|
||||
| |
|
||||
| `- 0
|
||||
|
|
||||
`- 0
|
||||
|
|
||||
+- 0
|
||||
|
|
||||
`- 0
|
||||
|
||||
#+END_EXAMPLE
|
||||
|
||||
Just to heat up your neurones a bit more, let's make a slightly more
|
||||
interesting tree:
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
|
||||
#+begin_src haskell :tangle infinite_tree_2.hs :exports none
|
||||
import Data.Tree (Tree,Forest(..))
|
||||
import qualified Data.Tree as Tree
|
||||
|
||||
data BinTree a = Empty
|
||||
| Node a (BinTree a) (BinTree a)
|
||||
deriving (Eq,Ord,Show)
|
||||
|
||||
-- | Function to transform our internal BinTree type to the
|
||||
-- type of Tree declared in Data.Tree (from containers package)
|
||||
-- so that the function Tree.drawForest can use
|
||||
binTreeToForestString :: (Show a) => BinTree a -> Forest String
|
||||
binTreeToForestString Empty = []
|
||||
binTreeToForestString (Node x left right) =
|
||||
[Tree.Node (show x) ((binTreeToForestString left) ++ (binTreeToForestString right))]
|
||||
|
||||
-- | Function that given a BinTree print a representation of it in the console
|
||||
prettyPrintTree :: (Show a) => BinTree a -> IO ()
|
||||
prettyPrintTree = putStrLn . Tree.drawForest . binTreeToForestString
|
||||
|
||||
-- | take all element of a BinTree up to some depth
|
||||
treeTakeDepth _ Empty = Empty
|
||||
treeTakeDepth 0 _ = Empty
|
||||
treeTakeDepth n (Node x left right) = let
|
||||
nl = treeTakeDepth (n-1) left
|
||||
nr = treeTakeDepth (n-1) right
|
||||
in
|
||||
Node x nl nr
|
||||
#+end_src
|
||||
|
||||
|
||||
#+BEGIN_SRC haskell :tangle infinite_tree_2.hs
|
||||
iTree = Node 0 (dec iTree) (inc iTree)
|
||||
where
|
||||
dec (Node x l r) = Node (x-1) (dec l) (dec r)
|
||||
|
@ -1986,7 +2058,7 @@ This function should be similar to =map=, but should work on =BinTree=
|
|||
instead of list.
|
||||
Here is such a function:
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
#+BEGIN_SRC haskell :tangle infinite_tree_2.hs
|
||||
-- apply a function to each node of Tree
|
||||
treeMap :: (a -> b) -> BinTree a -> BinTree b
|
||||
treeMap f Empty = Empty
|
||||
|
@ -2001,7 +2073,7 @@ structures, search for functor and =fmap=.
|
|||
|
||||
Our definition is now:
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
#+BEGIN_SRC haskell :tangle infinite_tree_2.hs
|
||||
infTreeTwo :: BinTree Int
|
||||
infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo)
|
||||
(treeMap (\x -> x+1) infTreeTwo)
|
||||
|
@ -2009,33 +2081,102 @@ Our definition is now:
|
|||
|
||||
Look at the result for
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
main = print $ treeTakeDepth 4 infTreeTwo
|
||||
{{{lnk(infinite_tree_2.hs)}}}
|
||||
#+BEGIN_SRC haskell :tangle infinite_tree_2.hs
|
||||
main = prettyPrintTree $ treeTakeDepth 4 infTreeTwo
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
< 0
|
||||
: |-- -1
|
||||
: | |-- -2
|
||||
: | | |-- -3
|
||||
: | | `-- -1
|
||||
: | `-- 0
|
||||
: | |-- -1
|
||||
: | `-- 1
|
||||
: `-- 1
|
||||
: |-- 0
|
||||
: | |-- -1
|
||||
: | `-- 1
|
||||
: `-- 2
|
||||
: |-- 1
|
||||
: `-- 3
|
||||
[hs:hsenv]> runghc infinite_tree_2.hs
|
||||
0
|
||||
|
|
||||
+- -1
|
||||
| |
|
||||
| +- -2
|
||||
| | |
|
||||
| | +- -3
|
||||
| | |
|
||||
| | `- -1
|
||||
| |
|
||||
| `- 0
|
||||
| |
|
||||
| +- -1
|
||||
| |
|
||||
| `- 1
|
||||
|
|
||||
`- 1
|
||||
|
|
||||
+- 0
|
||||
| |
|
||||
| +- -1
|
||||
| |
|
||||
| `- 1
|
||||
|
|
||||
`- 2
|
||||
|
|
||||
+- 1
|
||||
|
|
||||
`- 3
|
||||
#+END_EXAMPLE
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
main = do
|
||||
print $ treeTakeDepth 4 nullTree
|
||||
print $ treeTakeDepth 4 infTreeTwo
|
||||
#+END_SRC
|
||||
#+begin_notes
|
||||
The important things to remember.
|
||||
Haskell handle infinite structures naturally mostly because it is not strict.
|
||||
|
||||
So you can write, infinite tree, but also, you can generate infinite list
|
||||
like this common example:
|
||||
|
||||
#+begin_src haskell :tangle fib_lazy.hs
|
||||
fib :: [Integer]
|
||||
fib = 1:1:zipWith (+) fib (tail 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.
|
||||
|
||||
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
|
||||
number even considering the code looks a bit like a double recursion.
|
||||
|
||||
#+begin_example
|
||||
[hs:0010-Haskell-Now]> time runghc fib_lazy.hs
|
||||
453973694165307953197296969697410619233826
|
||||
734544867157818093234908902110449296423351
|
||||
1188518561323126046432205871807859915657177
|
||||
1923063428480944139667114773918309212080528
|
||||
3111581989804070186099320645726169127737705
|
||||
5034645418285014325766435419644478339818233
|
||||
8146227408089084511865756065370647467555938
|
||||
13180872826374098837632191485015125807374171
|
||||
21327100234463183349497947550385773274930109
|
||||
34507973060837282187130139035400899082304280
|
||||
55835073295300465536628086585786672357234389
|
||||
90343046356137747723758225621187571439538669
|
||||
146178119651438213260386312206974243796773058
|
||||
236521166007575960984144537828161815236311727
|
||||
382699285659014174244530850035136059033084785
|
||||
619220451666590135228675387863297874269396512
|
||||
1001919737325604309473206237898433933302481297
|
||||
1621140188992194444701881625761731807571877809
|
||||
2623059926317798754175087863660165740874359106
|
||||
4244200115309993198876969489421897548446236915
|
||||
|
||||
real 0m1.000s
|
||||
user 0m0.192s
|
||||
sys 0m0.058s
|
||||
#+end_example
|
||||
|
||||
#+end_notes
|
||||
|
||||
* Difficulty: Hard
|
||||
:PROPERTIES:
|
||||
|
@ -2043,7 +2184,7 @@ Look at the result for
|
|||
:END:
|
||||
|
||||
Congratulations for getting so far!
|
||||
Now, some of the really hardcore stuff can start.
|
||||
Now, some of the really hard stuff can start.
|
||||
|
||||
If you are like me, you should get the functional style.
|
||||
You should also understand a bit more the advantages of laziness by
|
||||
|
@ -2067,7 +2208,7 @@ But they are all very rewarding.
|
|||
[[./magritte_carte_blanche.jpg]]
|
||||
|
||||
#+BEGIN_QUOTE
|
||||
tldr;
|
||||
{{{tldr}}}
|
||||
|
||||
A typical function doing =IO= looks a lot like an imperative program:
|
||||
|
||||
|
@ -2083,11 +2224,14 @@ But they are all very rewarding.
|
|||
- To set a value to an object we use =<-= .
|
||||
- The type of each line is =IO *=; in this example:
|
||||
|
||||
- =action1 :: IO b=
|
||||
- =action2 x :: IO ()=
|
||||
- =action3 :: IO c=
|
||||
- =action4 x y :: IO a=
|
||||
- =x :: b=, =y :: c=
|
||||
#+begin_src haskell
|
||||
- action1 :: IO b
|
||||
- x :: b
|
||||
- action2 x :: IO ()
|
||||
- action3 :: IO c
|
||||
- y :: c
|
||||
- action4 x y :: IO a
|
||||
#+end_src
|
||||
|
||||
- Few objects have the type =IO a=, this should help you choose. In
|
||||
particular you cannot use pure functions directly here. To use pure
|
||||
|
@ -2108,7 +2252,8 @@ Ask a user to enter a list of numbers.
|
|||
Print the sum of the numbers.
|
||||
#+END_QUOTE
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
{{{lnk(io_sum.hs)}}}
|
||||
#+BEGIN_SRC haskell :tangle io_sum.hs
|
||||
toList :: String -> [Integer]
|
||||
toList input = read ("[" ++ input ++ "]")
|
||||
|
||||
|
@ -2172,7 +2317,7 @@ For example, what happens if the user enters something strange?
|
|||
Let's try:
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
% runghc 02_progressive_io_example.lhs
|
||||
[hs:hsenv]> runghc io_sum.hs
|
||||
Enter a list of numbers (separated by comma):
|
||||
foo
|
||||
Prelude.read: no parse
|
||||
|
@ -2187,7 +2332,7 @@ In order to do this, we must detect that something went wrong.
|
|||
Here is one way to do this: use the type =Maybe=.
|
||||
This is a very common type in Haskell.
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
#+BEGIN_SRC haskell :tangle io_sum_safe.hs
|
||||
import Data.Maybe
|
||||
#+END_SRC
|
||||
|
||||
|
@ -2208,7 +2353,7 @@ 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
|
||||
#+BEGIN_SRC haskell :tangle io_sum_safe.hs
|
||||
maybeRead :: Read a => String -> Maybe a
|
||||
maybeRead s = case reads s of
|
||||
[(x,"")] -> Just x
|
||||
|
@ -2219,14 +2364,14 @@ 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=.
|
||||
Otherwise, for example for "1,2,3", it will return =Just [1,2,3]=.
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
#+BEGIN_SRC haskell :tangle io_sum_safe.hs
|
||||
getListFromString :: String -> Maybe [Integer]
|
||||
getListFromString str = maybeRead $ "[" ++ str ++ "]"
|
||||
#+END_SRC
|
||||
|
||||
We simply have to test the value in our main function.
|
||||
|
||||
#+BEGIN_SRC haskell
|
||||
#+BEGIN_SRC haskell :tangle io_sum_safe.hs
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn "Enter a list of numbers (separated by comma):"
|
||||
|
@ -2234,15 +2379,13 @@ We simply have to test the value in our main function.
|
|||
let maybeList = getListFromString input in
|
||||
case maybeList of
|
||||
Just l -> print (sum l)
|
||||
Nothing -> error "Bad format. Good Bye."
|
||||
Nothing -> putStrLn "Bad format. Good Bye."
|
||||
#+END_SRC
|
||||
|
||||
In case of error, we display a nice error message.
|
||||
|
||||
Note that the type of each expression in the main's =do= block remains of
|
||||
the form =IO a=.
|
||||
The only strange construction is =error=.
|
||||
I'll just say here that =error msg= takes the needed type (here =IO ()=).
|
||||
|
||||
One very important thing to note is the type of all the functions defined
|
||||
so far.
|
||||
|
|
32
src/posts/0010-Haskell-Now/infinite_tree.hs
Normal file
32
src/posts/0010-Haskell-Now/infinite_tree.hs
Normal file
|
@ -0,0 +1,32 @@
|
|||
import Data.Tree (Tree,Forest(..))
|
||||
import qualified Data.Tree as Tree
|
||||
|
||||
data BinTree a = Empty
|
||||
| Node a (BinTree a) (BinTree a)
|
||||
deriving (Eq,Ord,Show)
|
||||
|
||||
-- | Function to transform our internal BinTree type to the
|
||||
-- type of Tree declared in Data.Tree (from containers package)
|
||||
-- so that the function Tree.drawForest can use
|
||||
binTreeToForestString :: (Show a) => BinTree a -> Forest String
|
||||
binTreeToForestString Empty = []
|
||||
binTreeToForestString (Node x left right) =
|
||||
[Tree.Node (show x) ((binTreeToForestString left) ++ (binTreeToForestString right))]
|
||||
|
||||
-- | Function that given a BinTree print a representation of it in the console
|
||||
prettyPrintTree :: (Show a) => BinTree a -> IO ()
|
||||
prettyPrintTree = putStrLn . Tree.drawForest . binTreeToForestString
|
||||
|
||||
nullTree = Node 0 nullTree nullTree
|
||||
|
||||
-- take all element of a BinTree
|
||||
-- up to some depth
|
||||
treeTakeDepth _ Empty = Empty
|
||||
treeTakeDepth 0 _ = Empty
|
||||
treeTakeDepth n (Node x left right) = let
|
||||
nl = treeTakeDepth (n-1) left
|
||||
nr = treeTakeDepth (n-1) right
|
||||
in
|
||||
Node x nl nr
|
||||
|
||||
main = prettyPrintTree (treeTakeDepth 4 nullTree)
|
45
src/posts/0010-Haskell-Now/infinite_tree_2.hs
Normal file
45
src/posts/0010-Haskell-Now/infinite_tree_2.hs
Normal file
|
@ -0,0 +1,45 @@
|
|||
import Data.Tree (Tree,Forest(..))
|
||||
import qualified Data.Tree as Tree
|
||||
|
||||
data BinTree a = Empty
|
||||
| Node a (BinTree a) (BinTree a)
|
||||
deriving (Eq,Ord,Show)
|
||||
|
||||
-- | Function to transform our internal BinTree type to the
|
||||
-- type of Tree declared in Data.Tree (from containers package)
|
||||
-- so that the function Tree.drawForest can use
|
||||
binTreeToForestString :: (Show a) => BinTree a -> Forest String
|
||||
binTreeToForestString Empty = []
|
||||
binTreeToForestString (Node x left right) =
|
||||
[Tree.Node (show x) ((binTreeToForestString left) ++ (binTreeToForestString right))]
|
||||
|
||||
-- | Function that given a BinTree print a representation of it in the console
|
||||
prettyPrintTree :: (Show a) => BinTree a -> IO ()
|
||||
prettyPrintTree = putStrLn . Tree.drawForest . binTreeToForestString
|
||||
|
||||
-- | take all element of a BinTree up to some depth
|
||||
treeTakeDepth _ Empty = Empty
|
||||
treeTakeDepth 0 _ = Empty
|
||||
treeTakeDepth n (Node x left right) = let
|
||||
nl = treeTakeDepth (n-1) left
|
||||
nr = treeTakeDepth (n-1) right
|
||||
in
|
||||
Node x nl nr
|
||||
|
||||
iTree = Node 0 (dec iTree) (inc iTree)
|
||||
where
|
||||
dec (Node x l r) = Node (x-1) (dec l) (dec r)
|
||||
inc (Node x l r) = Node (x+1) (inc l) (inc r)
|
||||
|
||||
-- apply a function to each node of Tree
|
||||
treeMap :: (a -> b) -> BinTree a -> BinTree b
|
||||
treeMap f Empty = Empty
|
||||
treeMap f (Node x left right) = Node (f x)
|
||||
(treeMap f left)
|
||||
(treeMap f right)
|
||||
|
||||
infTreeTwo :: BinTree Int
|
||||
infTreeTwo = Node 0 (treeMap (\x -> x-1) infTreeTwo)
|
||||
(treeMap (\x -> x+1) infTreeTwo)
|
||||
|
||||
main = prettyPrintTree $ treeTakeDepth 4 infTreeTwo
|
7
src/posts/0010-Haskell-Now/io_sum.hs
Normal file
7
src/posts/0010-Haskell-Now/io_sum.hs
Normal file
|
@ -0,0 +1,7 @@
|
|||
toList :: String -> [Integer]
|
||||
toList input = read ("[" ++ input ++ "]")
|
||||
|
||||
main = do
|
||||
putStrLn "Enter a list of numbers (separated by comma):"
|
||||
input <- getLine
|
||||
print $ sum (toList input)
|
18
src/posts/0010-Haskell-Now/io_sum_safe.hs
Normal file
18
src/posts/0010-Haskell-Now/io_sum_safe.hs
Normal file
|
@ -0,0 +1,18 @@
|
|||
import Data.Maybe
|
||||
|
||||
maybeRead :: Read a => String -> Maybe a
|
||||
maybeRead s = case reads s of
|
||||
[(x,"")] -> Just x
|
||||
_ -> Nothing
|
||||
|
||||
getListFromString :: String -> Maybe [Integer]
|
||||
getListFromString str = maybeRead $ "[" ++ str ++ "]"
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn "Enter a list of numbers (separated by comma):"
|
||||
input <- getLine
|
||||
let maybeList = getListFromString input in
|
||||
case maybeList of
|
||||
Just l -> print (sum l)
|
||||
Nothing -> putStrLn "Bad format. Good Bye."
|
|
@ -19,4 +19,7 @@ in
|
|||
pkgs.stdenv.mkDerivation {
|
||||
name = "env";
|
||||
buildInputs = nixPackages;
|
||||
shellHook = ''
|
||||
export PS1="\n[hs:\033[1;32m\]\W\[\033[0m\]]> "
|
||||
'';
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue