From bc108b581ffebee02eb49ccf1144bffb87af7e68 Mon Sep 17 00:00:00 2001 From: "Yann Esposito (Yogsototh)" Date: Thu, 21 Jun 2018 22:02:57 +0200 Subject: [PATCH] Simple update --- 1_Introduction.org | 2 +- 2_Install.org | 58 +----------- 3_Intermediate.org | 217 +++++++++++++++++++++++---------------------- code/guess-1.hs | 29 ++++++ 4 files changed, 142 insertions(+), 164 deletions(-) create mode 100755 code/guess-1.hs diff --git a/1_Introduction.org b/1_Introduction.org index 2d23b6b..72cc36f 100644 --- a/1_Introduction.org +++ b/1_Introduction.org @@ -107,7 +107,7 @@ This book is very opinionated, because I wanted to be efficient in learning fast for some specific kind of personalities. It might not be for you. -One of my goal is to shortcicuit some classic learning detour. +One of my goal is to shortcircuit some classic learning detour. For a lot of decisions I generally make only one choice. I'll try to talk about the other choices and it will be your duty to explore other choices after you diff --git a/2_Install.org b/2_Install.org index f3612e3..af291d7 100644 --- a/2_Install.org +++ b/2_Install.org @@ -62,62 +62,7 @@ curl -sSL https://get.haskellstack.org/ | sh *** TO-CLEAN git -You should have [[https://git-scm.com][=git=]] installed. - -*** TO-CLEAN Stack template - -Before starting to write your first line of code. -Let's create a project with a sane and modern file organisation. - -I made a stack templates largely inspired by tasty-travis template. It will -provide a bootstrap for organizing your application with tests, benchmarks and -continuous integration. - -This template provide a file organisation for your projects. - -Mainly do jump into programmin you could theoretically just download the binary -of the main Haskell compiler GHC to your compiler and compile each file with -=ghc myfile.hs=. But let's face it. It's not suitable for real project which -need more informations about it. - -So let's start with a sane professional organisation for your files. - -#+BEGIN_SRC shell -stack new myproject https://git.io/vbpej -#+END_SRC - -After that, this should generate a new `myproject` directory with the following -files: - -#+BEGIN_SRC -> tree -. -├── CHANGELOG.md -├── LICENSE -├── README.md -├── Setup.hs -├── myproject.cabal -├── package.yaml -├── src -│ └── Lib.hs -├── src-benchmark -│ └── Main.hs -├── src-doctest -│ └── Main.hs -├── src-exe -│ └── Main.hs -├── src-test -│ └── Main.hs -├── stack.yaml -└── tutorial.md - -5 directories, 13 files -#+END_SRC - -Most of your source code should be in the =src= directory. Generally =src-exe= -should be a minimal code that could handle the =main= function to start your -application. We'll talk about other part later in the book but most other file -should be quite straightforward. +You should have =[[https://git-scm.com][git]]= installed. *** TO-CLEAN Editor @@ -173,7 +118,6 @@ I think you can change the value of =dotspacemacs-editing-style= from ='vim= to ='hybrid= or ='emacs= in the =.spacemacs= file. It should be good now. - *** TO-CLEAN Conclusion First you can congratulate yourself to have installed all the prerequiste to diff --git a/3_Intermediate.org b/3_Intermediate.org index aafe9a1..edd33fd 100644 --- a/3_Intermediate.org +++ b/3_Intermediate.org @@ -151,14 +151,37 @@ main = do putText ("Hello " <> name <> "!") #+END_SRC -We can try that in the REPL (GHCI). You shold be able to start it from your +We can try that in the REPL (GHCI). You should be able to start it from your editor. For example in spacemacs I can load the current buffer (open file) in the REPL with =SPC m s b=. You could also start the repl in a terminal with =stack ghci= -And then load the module with =:l src-exe/Main=. +And then load the module with =:l hello_name.hs=. The =:l= is a shortcut for =:load=. +#+BEGIN_SRC +> stack ghci + +Warning: No local targets specified, so ghci will not use any options from your package.yaml / *.cabal files. + + Potential ways to resolve this: + * If you want to use the package.yaml / *.cabal package in the current directory, use stack init to create a new stack.yaml. + * Add to the 'packages' field of ~/.stack/global-project/stack.yaml + +Configuring GHCi with the following packages: +GHCi, version 8.2.2: http://www.haskell.org/ghc/ :? for help +Loaded GHCi configuration from /private/var/folders/bp/_8thkcjd4k3g81mpxtkq44h80000gn/T/ghci70782/ghci-script +Prelude> :l hello_name.hs +[1 of 1] Compiling Main ( hello_name.hs, interpreted ) [flags changed] +Ok, one module loaded. +*Main> main +What is your name? +Yann +Hello Yann! +#+END_SRC + +But you should also simply run it from command line: + #+BEGIN_SRC > ./hello_name.sh What is your name? @@ -310,13 +333,13 @@ Here are all the possible choices: That is already 5 different choices. But there is another package that provide other string choices. In =Foundation= the strings are =UTF-8=. -Mmmm so much choices. +Hmmm... so much choices. -So to make it clear, in general, don't use =String= for anything serious. -Use =Text= most of the time. +A rule of thumbs is to never use =String= for anything serious. +Use =Text= most of the time because they support encoding. Use =Bytestring= if you need efficient bytes arrays. -By using Protolude, we prevent the use of =String= naturally. +By using Protolude, we naturally don't use =String=. **** TO-CLEAN Write a guess my age program @@ -328,9 +351,18 @@ The =src-exe/Main.hs= should be very minimalist, so now let's change its content by: #+BEGIN_SRC haskell +#!/usr/bin/env stack +{- stack script + --resolver lts-11.6 + --install-ghc + --package protolude +-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} import Protolude -import Guess (guess) +guess :: IO () +guess = undefined main :: IO () main = do @@ -338,28 +370,23 @@ main = do putText "Thanks for playing!" #+END_SRC -Now we need to create the file =src/Guess.hs= which should declare the function -=guess=. Let's start with this content: - -#+BEGIN_SRC haskell -module Guess - ( guess - ) where - -import Protolude - -guess :: IO () -guess = undefined -#+END_SRC - -We declare a =Guess= module which use Protolude. We know that the type of guess must be =IO ()=. We don't know yet what the code will be so I just used =undefined=. This way the program will be able to typecheck. -So here is the program that will try to guess your age: +The next step is to define the ~guess~ function. + +#+BEGIN_SRC haskell :tangle code/guess-1.hs +#!/usr/bin/env stack +{- stack script + --resolver lts-11.6 + --install-ghc + --package protolude +-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +import Protolude -#+BEGIN_SRC haskell guess :: IO () guess = guessBetween 0 120 @@ -374,6 +401,11 @@ guessBetween minAge maxAge = do case answer of "y" -> guessBetween minAge (age - 1) _ -> guessBetween (if age == minAge then age + 1 else age) maxAge + +main :: IO () +main = do + guess + putText "Thanks for playing!" #+END_SRC So going from there we declared the =guess= function to call the =guessBetween= @@ -625,16 +657,67 @@ For that we will use the =optparse-generic= package. *** TODO File Access *** TODO Daemons & Logging ** TODO Intermediate -*** TO-CLEAN Init the project +*** TO-CLEAN Stack template ☞ As a first projet a lot of new concept will be introduced. Don't be discouraged by that. -#+BEGIN_SRC -> stack new guess https://git.io/vbpej -> cd guess +Let's create a project with a sane and modern file organisation. + +I made a stack templates largely inspired by =tasty-travis= template. It will +provide a bootstrap for organizing your application with tests, benchmarks and +continuous integration. + +This template provide a file organisation for your projects. + +Mainly do jump into programmin you could theoretically just download the binary +of the main Haskell compiler GHC to your compiler and compile each file with +=ghc myfile.hs=. But let's face it. It's not suitable for real project which +need more informations about it. + +So let's start with a sane professional organisation for your files. + +#+BEGIN_COMMENT +****** TODO modify the URL to use a better URL: torrent / IPFS +#+END_COMMENT + +#+BEGIN_SRC shell +stack new guess https://git.io/vbpej #+END_SRC +After that, this should generate a new ~guess~ directory with the following +files: + +#+BEGIN_SRC +> tree +. +├── CHANGELOG.md +├── LICENSE +├── README.md +├── Setup.hs +├── guess.cabal +├── package.yaml +├── src +│ └── Lib.hs +├── src-benchmark +│ └── Main.hs +├── src-doctest +│ └── Main.hs +├── src-exe +│ └── Main.hs +├── src-test +│ └── Main.hs +├── stack.yaml +└── tutorial.md + +5 directories, 13 files +#+END_SRC + +Most of your source code should be in the =src= directory. Generally =src-exe= +should be a minimal code that could handle the =main= function to start your +application. We'll talk about other parts later in the book but most other file +should be quite straightforward. + Edit the file =src-exe/Main.hs= The file contains: @@ -658,84 +741,6 @@ To compile it do a So that program print 42 and stop. -Let's go line by line about what is occuring here. - -- =import Protolude=: Haskell is a language, but you need to start with some - useful definition for you. And Protolude is a /prelude/. That mean that it - provides a list of useful definitions for you. By default you don't need to - import manually the default prelude. But the more you work with Haskell the - more you feel the need to write your own prelude. =Protolude= is a sane - minimal and professional starting point. -- =import Lib (inc)=: That line means that we import the function =inc= from - another module named =Lib= The module correspond to the file =src/Lib.hs=. No - need to look into it now. -- =main :: IO ()=: - This is the declaration of the type of the =main= function. - The main function will be the function called when you launch your - application. Excatly like in =C=. The type is =IO ()=. It means that - =main= will make some interaction with the system and will return - nothing. -- =main = print . inc $ (41 :: Int)= -For that line there are in fact a lot of things going on. - -We define the function =main=. -Lets compare this notation with other programming languages: - -C: - -#+BEGIN_SRC c -void main () { - ... -} -#+END_SRC - -javascript: - -#+BEGIN_SRC javascript -function main() { - ... -} -#+END_SRC - -python: - -#+BEGIN_SRC python -def main: - ... -#+END_SRC - -LISP: - -#+BEGIN_SRC elisp -(define foo () ...) -#+END_SRC - -Clojure: - -#+BEGIN_SRC clojure -(defn main [] ...) -#+END_SRC - -Now take a look at the content: - -#+BEGIN_SRC haskell -main = print (inc 41) -#+END_SRC - -☞ A very important syntax detail; -function application is done with a simple space. -So =foo bar= means you apply the function =foo= to the parameter =bar=. -And by default the priority is on the left. -So: =foo bar baz= is equivalent to =(foo bar) baz=. - -While that notation is quite simpler it can take some time to be used to it and -to parse it naturally. - -So here we first call =inc= on =41= wich is =42=. -Then we print it to the standard output. - -And that's it. - *** TODO DB Access **** NoSQL (Redis looks easy) **** Stream DB (Kafka or NATS, etc...) diff --git a/code/guess-1.hs b/code/guess-1.hs new file mode 100755 index 0000000..1ecf734 --- /dev/null +++ b/code/guess-1.hs @@ -0,0 +1,29 @@ +#!/usr/bin/env stack +{- stack script + --resolver lts-11.6 + --install-ghc + --package protolude +-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +import Protolude + +guess :: IO () +guess = guessBetween 0 120 + +guessBetween :: Integer -> Integer -> IO () +guessBetween minAge maxAge = do + let age = (maxAge + minAge) `div` 2 + if minAge == maxAge + then putText ("You are " <> show minAge) + else do + putText ("Are you younger than " <> show age <> "?") + answer <- getLine + case answer of + "y" -> guessBetween minAge (age - 1) + _ -> guessBetween (if age == minAge then age + 1 else age) maxAge + +main :: IO () +main = do + guess + putText "Thanks for playing!"