Simple update
This commit is contained in:
parent
44e1b7805c
commit
bc108b581f
4 changed files with 142 additions and 164 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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...)
|
||||
|
|
29
code/guess-1.hs
Executable file
29
code/guess-1.hs
Executable file
|
@ -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!"
|
Loading…
Reference in a new issue