diff --git a/src/posts/0010-Haskell-Now/basic.hs b/src/posts/0010-Haskell-Now/basic.hs
new file mode 100644
index 0000000..6a14ccf
--- /dev/null
+++ b/src/posts/0010-Haskell-Now/basic.hs
@@ -0,0 +1,4 @@
+f :: Int -> Int -> Int
+f x y = x*x + y*y
+
+main = print (f 2 3)
diff --git a/src/posts/0010-Haskell-Now/error_basic.hs b/src/posts/0010-Haskell-Now/error_basic.hs
new file mode 100644
index 0000000..b3e87c0
--- /dev/null
+++ b/src/posts/0010-Haskell-Now/error_basic.hs
@@ -0,0 +1,4 @@
+f :: Int -> Int -> Int
+f x y = x*x + y*y
+
+main = print (f 2.3 4.2)
diff --git a/src/posts/0010-Haskell-Now/float_basic.hs b/src/posts/0010-Haskell-Now/float_basic.hs
new file mode 100644
index 0000000..0947218
--- /dev/null
+++ b/src/posts/0010-Haskell-Now/float_basic.hs
@@ -0,0 +1,3 @@
+f x y = x*x + y*y
+
+main = print (f 2.3 4.2)
diff --git a/src/posts/0010-Haskell-Now/hello.hs b/src/posts/0010-Haskell-Now/hello.hs
new file mode 100644
index 0000000..d5e55cc
--- /dev/null
+++ b/src/posts/0010-Haskell-Now/hello.hs
@@ -0,0 +1 @@
+main = putStrLn "Hello World!"
diff --git a/src/posts/0010-Haskell-Now/index.org b/src/posts/0010-Haskell-Now/index.org
index 4302067..771324b 100644
--- a/src/posts/0010-Haskell-Now/index.org
+++ b/src/posts/0010-Haskell-Now/index.org
@@ -164,6 +164,12 @@ The article contains five parts:
- More on infinite tree; a more math oriented discussion about
infinite trees
+** Helpers :noexport:
+:PROPERTIES:
+:CUSTOM_ID: helpers
+:END:
+
+#+MACRO: lnk @@html:$1 ⤓@@
** Install
@@ -174,122 +180,72 @@ The article contains five parts:
#+CAPTION: Haskell logo
[[./Haskell-logo.png]]
-There are multiple way to install Haskell and I don't think there is a full
-consensus between developer about what is the best method.
-
-For this tutorial, I expect you to have either installed the [[https://nixos.org/nix][nix]] package
-manager or to have installed [[https://haskellstack.org][=stack=]].
-
-The easiest method would certainly to use [[https://nixos.org/nix][nix]].
-
-*** Nix
-:PROPERTIES:
-:CUSTOM_ID: nix
-:END:
-
1. Install [[https://nixos.org/nix][nix]]
-2. Write the following =shell.nix= file:
+2. create a new empty directory =hsenv= somewhere
+3. Put the following =shell.nix= file inside it
{{{lnk(shell.nix)}}}
#+begin_src nix :tangle shell.nix
- { pkgs ? import (fetchTarball
- https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz) {} }:
- pkgs.mkShell {
- buildInputs =
- with pkgs;
- with pkgs.haskellPackages; [
- ghc
- cabal-install
- zsh
- protolude
- ];
- }
+ { nixpkgs ? import (fetchTarball https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz) {} }:
+ let
+ inherit (nixpkgs) pkgs;
+ inherit (pkgs) haskellPackages;
+
+ haskellDeps = ps: with ps; [
+ base
+ protolude
+ ];
+
+ ghc = haskellPackages.ghcWithPackages haskellDeps;
+
+ nixPackages = [
+ ghc
+ pkgs.gdb
+ haskellPackages.cabal-install
+ ];
+ in
+ pkgs.stdenv.mkDerivation {
+ name = "env";
+ buildInputs = nixPackages;
+ }
#+end_src
-3. In the same directory as the file in a terminal run =nix-shell=.
+4. In the =hsenv= directory, in a terminal, run =nix-shell=.
You should wait a lot of time for everything to download.
And you should be ready.
You will have in your PATH:
- =ghc=, the Haskell compiler
- =ghci= that we can described as a Haskell REPL
- =runghc= that will be able to interpret a Haskell file
+ And you all those tools will be able to use the Haskell library
+ /protolude/.
+5. To test your env, rung =ghci= and type =import Protolude= you should see
+ something like this:
+
+ #+begin_src
+ ~/hsenv> nix-shell
+ [nix-shell:~/hsenv]$ ghci
+ GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help
+ Prelude> import Protolude
+ Prelude Protolude>
+ #+end_src
+
+Congratulations you should be ready to start now.
#+begin_notes
-The only really important parts for you will be the two lists, one for
-system dependencies and one for Haskell packages.
-
-=nix= is a generic package manager and goes beyond Haskell.
-Has such you can add zsh in your dependency for example.
+- There are multiple ways to install Haskell and I don't think there is a
+ full consensus between developer about what is the best method. If you
+ whish to use another method take a look at [[http://haskell.org][haskell.org]].
+- This install method is only suitable for using as a playground and
+ perfect for this tutorial. I don't think it is suitable for serious
+ development.
+- =nix= is a generic package manager and goes beyond Haskell. One great
+ good point is that it does not only manage Haskell packages but really a
+ lot of other kind of packages. This can be quite helpful if you need to
+ depends on a Haskell package that itself depends on a system library, for
+ example =ncurses=.
#+end_notes
-*** Executable
-:PROPERTIES:
-:CUSTOM_ID: executable
-:END:
-
-With those two method I can provide you a bang pattern to create self
-executable script that will use the Haskell compiler I expect and hopefully
-all the code example should still work for a _very_ long time.
-
-For other ways to install Haskell on your system you should visit
-[[https://haskell.org][haskell.org]].
-
-The environment in which you will learn Haskell will be quite different
-from an environment to use Haskell seriously for a new project.
-This is because, there are too much choices for that.
-
-Mainly, you can start by writing your code in a file and executing it by
-putting one of the following at the top of your file:
-
-If you chose Nix: https://nixos.org/nix/
-
-#+BEGIN_EXAMPLE
-#! /usr/bin/env nix-shell
-#! nix-shell -i runghc
-#! nix-shell -p "ghc.withPackages (ps: [ ps.protolude ])"
-#! nix-shell -I nixpkgs="https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz"
-#+END_EXAMPLE
-
-
-If you chose Stack: https://haskellstack.org
-
-#+BEGIN_EXAMPLE haskell
-#!/usr/bin/env stack
-{- stack script
- --resolver lts-14.16
- --install-ghc
- --package protolude
--}
-#+END_EXAMPLE
-
-In this article most code block can be downloaded, it will have the =nix=
-shebang.
-
-So the first time you'll launch this script it will download all
-dependencies for you and will start its execution.
-
-The next time it should start a lot faster.
-
-*** code :noexport:
-:PROPERTIES:
-:CUSTOM_ID: code
-:END:
-
-#+begin_src elisp :eval yes
- (defun nixb ()
- (mapconcat 'identity
- '("#! /usr/bin/env nix-shell"
- "#! nix-shell -i runghc"
- "#! nix-shell -p \"ghc.withPackages (ps: [ ps.protolude ])\""
- "#! nix-shell -I nixpkgs=\"https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz\"")
- "\n"))
-#+end_src
-
-#+RESULTS:
-: nixb
-
-#+MACRO: lnk @@html:$1 ⤓@@
-
** Don't be afraid
:PROPERTIES:
:CUSTOM_ID: don't-be-afraid
@@ -305,19 +261,12 @@ similarities between Haskell and other programming languages. Let's jump
to the mandatory "Hello World".
{{{lnk(hello.hs)}}}
-#+BEGIN_SRC haskell :tangle hello.hs :shebang '(nixb)
+#+BEGIN_SRC haskell :tangle hello.hs
main = putStrLn "Hello World!"
#+END_SRC
#+BEGIN_EXAMPLE
-> chmod +x hello.hs
-> ./hello.hs
-Hello World!
-#+END_EXAMPLE
-
-#+BEGIN_EXAMPLE
-> stack ghc -- hello.hs
-> ./hello
+~ runghc hello.hs
Hello World!
#+END_EXAMPLE
@@ -325,7 +274,7 @@ Now, a program asking your name and replying "Hello" using the name you
entered:
{{{lnk(name.hs)}}}
-#+BEGIN_SRC haskell :tangle name.hs :shebang '(nixb)
+#+BEGIN_SRC haskell :tangle name.hs
main = do
print "What is your name?"
name <- getLine
@@ -362,17 +311,16 @@ int main (int argc, char **argv) {
}
#+END_SRC
-The structure is the same, but there are some syntax differences. The
-main part of this tutorial will be dedicated to explaining why.
+The structure is the same, but there are some syntax differences.
+The main part of this tutorial will be dedicated to explaining why.
In Haskell there is a =main= function and every object has a type. The
-type of =main= is =IO ()=. This means =main= will cause side effects.
+type of =main= is =IO ()=.
+This means =main= will cause side effects.
Just remember that Haskell can look a lot like mainstream imperative
languages.
------
-
** Very basic Haskell
:PROPERTIES:
:CUSTOM_ID: very-basic-haskell
@@ -479,9 +427,9 @@ Finally, the Haskell way is:
Very clean. No parenthesis, no =def=.
-Don't forget, Haskell uses functions and types a lot. It is thus very
-easy to define them. The syntax was particularly well thought out for
-these objects.
+Don't forget, Haskell uses functions and types a lot.
+It is thus very easy to define them.
+The syntax was particularly well thought out for these objects.
*** A Type Example
:PROPERTIES:
@@ -489,13 +437,16 @@ these objects.
:END:
Although it is not mandatory, type information for functions is usually
-made explicit. It's not mandatory because the compiler is smart enough
-to discover it for you. It's a good idea because it indicates intent and
-understanding.
+made explicit.
+It's not mandatory because the compiler is smart enough to infer it for
+you.
+It's a good idea because it indicates intent and understanding.
-Let's play a little. We declare the type using =::=
+Let's play a little.
+We declare the type using =::=
-#+BEGIN_SRC haskell
+{{{lnk(basic.hs)}}}
+#+BEGIN_SRC haskell :tangle basic.hs
f :: Int -> Int -> Int
f x y = x*x + y*y
@@ -503,15 +454,14 @@ Let's play a little. We declare the type using =::=
#+END_SRC
#+BEGIN_EXAMPLE
-~ runhaskell 20_very_basic.lhs
+[nix-shell:~/tmp/hsenv]$ runghc basic.hs
13
#+END_EXAMPLE
------
-
Now try
-#+BEGIN_SRC haskell
+{{{lnk(error_basic.hs)}}}
+#+BEGIN_SRC haskell :tangle error_basic.hs
f :: Int -> Int -> Int
f x y = x*x + y*y
@@ -521,28 +471,35 @@ Now try
You should get this error:
#+BEGIN_EXAMPLE
-21_very_basic.lhs:6:23:
- No instance for (Fractional Int)
- arising from the literal `4.2'
- Possible fix: add an instance declaration for (Fractional Int)
- In the second argument of `f', namely `4.2'
- In the first argument of `print', namely `(f 2.3 4.2)'
- In the expression: print (f 2.3 4.2)
+[nix-shell:~/tmp/hsenv]$ runghc error_basic.hs
+
+error_basic.hs:4:17: error:
+ • No instance for (Fractional Int) arising from the literal ‘2.3’
+ • In the first argument of ‘f’, namely ‘2.3’
+ In the first argument of ‘print’, namely ‘(f 2.3 4.2)’
+ In the expression: print (f 2.3 4.2)
+ |
+4 | main = print (f 2.3 4.2)
+ | ^^^
#+END_EXAMPLE
The problem: =4.2= isn't an Int.
------
-
The solution: don't declare a type for =f= for the moment and let
Haskell infer the most general type for us:
-#+BEGIN_SRC haskell
+{{{lnk(float_basic.hs)}}}
+#+BEGIN_SRC haskell :tangle float_basic.hs
f x y = x*x + y*y
main = print (f 2.3 4.2)
#+END_SRC
+#+begin_example
+[nix-shell:~/tmp/hsenv]$ runghc float_basic.hs
+22.93
+#+end_example
+
It works! Luckily, we don't have to declare a new function for every
single type. For example, in =C=, you'll have to declare a function for
=int=, for =float=, for =long=, for =double=, etc...
@@ -580,39 +537,44 @@ just look at a list of progressive examples:
| =a -> a= | the type function from any type =a= to the same type =a= |
| =a -> a -> a= | the type function of two arguments of any type =a= to the same type =a= |
-In the type =a -> a -> a=, the letter =a= is a /type variable/. It means
-=f= is a function with two arguments and both arguments and the result
-have the same type. The type variable =a= could take many different type
-values. For example =Int=, =Integer=, =Float=...
+In the type =a -> a -> a=, the letter =a= is a /type variable/.
+It means =f= is a function with two arguments and both arguments and the
+result have the same type.
+The type variable =a= could take many different type values.
+For example =Int=, =Integer=, =Float=...
So instead of having a forced type like in =C= and having to declare a
function for =int=, =long=, =float=, =double=, etc., we declare only one
function like in a dynamically typed language.
-This is sometimes called parametric polymorphism. It's also called
-having your cake and eating it too.
+This is sometimes called parametric polymorphism.
+It's also called having your cake and eating it too.
Generally =a= can be any type, for example a =String= or an =Int=, but
-also more complex types, like =Trees=, other functions, etc. But here
-our type is prefixed with =Num a =>=.
+also more complex types, like =Trees=, other functions, etc...
+But here our type is prefixed with =Num a =>=.
-=Num= is a /type class/. A type class can be understood as a set of
-types. =Num= contains only types which behave like numbers. More
-precisely, =Num= is class containing types which implement a specific
+=Num= is a /type class/.
+A type class can be understood as a set of types.
+=Num= contains only types which behave like numbers.
+More precisely, =Num= is class containing types which implement a specific
list of functions, and in particular =(+)= and =(*)=.
-Type classes are a very powerful language construct. We can do some
-incredibly powerful stuff with this. More on this later.
+Type classes are a very powerful language construct.
+We can do some incredibly powerful stuff with this.
+More on this later.
Finally, =Num a => a -> a -> a= means:
Let =a= be a type belonging to the =Num= type class. This is a function
from type =a= to (=a -> a=).
-Yes, strange. In fact, in Haskell no function really has two arguments.
-Instead all functions have only one argument. But we will note that
-taking two arguments is equivalent to taking one argument and returning
-a function taking the second argument as a parameter.
+Yes, strange.
+In fact, in Haskell no function really has two arguments.
+Instead all functions have only one argument.
+But we will note that taking two arguments is equivalent to taking one
+argument and returning a function taking the second argument as a
+parameter.
More precisely =f 3 4= is equivalent to =(f 3) 4=. Note =f 3= is a
function:
@@ -626,9 +588,11 @@ function:
g y ⇔ 3*3 + y*y
#+END_SRC
-Another notation exists for functions. The lambda notation allows us to
-create functions without assigning them a name. We call them anonymous
-functions. We could also have written:
+Another notation exists for functions.
+The lambda notation allows us to create functions without assigning them a
+name.
+We call them anonymous functions.
+We could also have written:
#+BEGIN_SRC haskell
g = \y -> 3*3 + y*y
@@ -637,14 +601,14 @@ functions. We could also have written:
The =\= is used because it looks like =λ= and is ASCII.
If you are not used to functional programming your brain should be
-starting to heat up. It is time to make a real application.
-
------
+starting to heat up.
+It is time to make a real application.
But just before that, we should verify the type system works as
expected:
-#+BEGIN_SRC haskell
+{{{lnk(typed_float_basic.hs)}}}
+#+BEGIN_SRC haskell :tangle typed_float_basic.hs
f :: Num a => a -> a -> a
f x y = x*x + y*y
diff --git a/src/posts/0010-Haskell-Now/shell.nix b/src/posts/0010-Haskell-Now/shell.nix
new file mode 100644
index 0000000..8706197
--- /dev/null
+++ b/src/posts/0010-Haskell-Now/shell.nix
@@ -0,0 +1,22 @@
+{ nixpkgs ? import (fetchTarball https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz) {} }:
+let
+ inherit (nixpkgs) pkgs;
+ inherit (pkgs) haskellPackages;
+
+ haskellDeps = ps: with ps; [
+ base
+ protolude
+ ];
+
+ ghc = haskellPackages.ghcWithPackages haskellDeps;
+
+ nixPackages = [
+ ghc
+ pkgs.gdb
+ haskellPackages.cabal-install
+ ];
+in
+pkgs.stdenv.mkDerivation {
+ name = "env";
+ buildInputs = nixPackages;
+}
diff --git a/src/posts/0010-Haskell-Now/typed_float_basic.hs b/src/posts/0010-Haskell-Now/typed_float_basic.hs
new file mode 100644
index 0000000..97ab7a5
--- /dev/null
+++ b/src/posts/0010-Haskell-Now/typed_float_basic.hs
@@ -0,0 +1,4 @@
+f :: Num a => a -> a -> a
+f x y = x*x + y*y
+
+main = print (f 3 2.4)