Tryed to add more insight for Haskell types.

This commit is contained in:
Yann Esposito (Yogsototh) 2019-12-27 11:30:08 +01:00
parent 3f403f946b
commit c65b820e0a
Signed by untrusted user who does not match committer: yogsototh
GPG key ID: 7B19A4C650D59646
4 changed files with 149 additions and 68 deletions

View file

@ -162,7 +162,7 @@ The article contains five parts:
- More on infinite tree; a more math oriented discussion about
infinite trees
** Helpers :noexport:
** Helpers :noexport:
:PROPERTIES:
:CUSTOM_ID: helpers
:END:
@ -211,15 +211,15 @@ The article contains five parts:
}
#+end_src
4. In the =hsenv= directory, in a terminal, run =nix-shell=.
4. In the =hsenv= directory, in a terminal, run =nix-shell --pure=.
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/.
- =cabal= which is the main tool to deal with Haskell projects
- the Haskell libraries =protolude= and =containers=.
5. To test your env, rung =ghci= and type =import Protolude= you should see
something like this:
@ -1850,8 +1850,8 @@ Node 'f' Empty (Node 'o' Empty Empty)
#+end_example
Notice how duplicate elements aren't inserted in trees.
For exemple the Char BinTree constructed from the list =foo= is just =f ->
o=.
For exemple the Char BinTree constructed from the list =foo= is
just =f -> o=.
When =o= is inserted another time the second =o= is not duplicated.
But more importantly it works also for our own =BinTree= notice how the
tree for =foo= is inserted only once.
@ -1862,6 +1862,88 @@ See how awesome this structure is: we can make trees containing not only
integers, strings and chars, but also other trees.
And we can even make a tree containing a tree of trees!
*** More Advanced Types
:PROPERTIES:
:CUSTOM_ID: more-advanced-types
:END:
So far we have presented types that are close to types we can see in most
typed programming languages.
But the real strength of Haskell is its type system.
So I will try to give you an idea about what makes the Haskell type system
more advanced than in most languages.
So as comparison, classical types/schemas, etc... are about products of
different sub-types:
#+begin_src haskell
data ProductType = P Int String
data PersonRecord = Person { age :: Int, name :: String }
#+end_src
Haskell has also a notion of =sum types= that I often lack a lot in other
programming languages I use.
You can define your type as a sum:
#+begin_src haskell
data Point = D1 Int | D2 Int Int | D3 Int Int Int
#+end_src
So far so good.
Sum types are already a nice thing to have, in particular within Haskell
because now the compiler can warn you if you miss a case.
For example if you write:
#+begin_src haskell
case point of
D1 x -> ...
D2 x y -> ...
#+end_src
If you compile with the =-Wall= flag (as you should always do for serious
development) then the compiler will warn you that you are forgetting some
possible value.
Those are still not really advanced types.
Advanced type are higher order types.
Those are the one that help with making your code more polymorphic.
We will start with example I alreday provided, lists:
#+begin_src haskell
data MyList a = Cons a (MyList a) | Nil
#+end_src
As you can see =MyList= takes a type parameter.
So =MyList= is a higher order type.
Generally, the intuition behind type is that a type is a data structure or
a container.
But in fact, Haskell types can be or can contain functions.
This is for example the case for =IO=.
And this is why it can be confusing to read the type of some functions.
I will take as example =sequenceA=:
#+begin_src haskell
sequenceA :: Applicative f => t (f a) -> f (t a)
#+end_src
So if you read this, it can be quite difficult to grasp what is the
intended use of this function.
A simple technique for example, is to try to replace the higher order types
(here =t= and =f=) by a type you can have some intuition about.
For example consider =t= to be the higher order type =Tree= and =f= to be
the higher order type =[]= (list).
Now you can see that =sequenceA= sill take a Tree of lists and will return
a list of trees.
For it to work =[]= need to be part of the =Applicative= class type (which
is the case).
I will not enter into the details about what =Applicative= type class is
here.
But just with this, you should start to have a better intuition about what
=sequenceA= is about.
** Infinite Structures
:PROPERTIES:
:CUSTOM_ID: infinite-structures
@ -3492,7 +3574,6 @@ your project, I still want to show how you could only use =cabal-install=.
This part will be for advanced Haskell code.
* Thanks
:PROPERTIES:
:CUSTOM_ID: thanks

View file

@ -1,21 +1,21 @@
import Data.Tree (Tree,Forest(..))
import qualified Data.Tree as Tree
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)
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 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
-- | 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

View file

@ -1,30 +1,30 @@
import Data.Tree (Tree,Forest(..))
import qualified Data.Tree as Tree
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)
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 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
-- | 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
-- | 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

View file

@ -1,26 +1,26 @@
{ nixpkgs ? import (fetchTarball https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz) {} }:
let
inherit (nixpkgs) pkgs;
inherit (pkgs) haskellPackages;
{ 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
containers
];
haskellDeps = ps: with ps; [
base
protolude
containers
];
ghc = haskellPackages.ghcWithPackages haskellDeps;
ghc = haskellPackages.ghcWithPackages haskellDeps;
nixPackages = [
ghc
pkgs.gdb
haskellPackages.cabal-install
];
in
pkgs.stdenv.mkDerivation {
name = "env";
buildInputs = nixPackages;
shellHook = ''
export PS1="\n[hs:\033[1;32m\]\W\[\033[0m\]]> "
'';
}
nixPackages = [
ghc
pkgs.gdb
haskellPackages.cabal-install
];
in
pkgs.stdenv.mkDerivation {
name = "env";
buildInputs = nixPackages;
shellHook = ''
export PS1="\n[hs:\033[1;32m\]\W\[\033[0m\]]> "
'';
}