This is my personal repository learning Haskell
from Haskell Programming
-
First, this is my personal journey learning
Haskell
therefore any mistakes on concepts and/or chapter exercises are my own. -
Second, I came to this
Haskell
journey because of my curiosity in FP that began mid-2016 when I was working on a UI story/task in Angular and used these JS libraries, RamdaJS, RxJS and ReduxJS, that are primarily developed with FP concepts in mind, such as immutability, composition, high-order function etc.Since then my
Haskell
journey has been on-and-off, until the fall season of 2018 that I picked up this book to learn. I can say that the authors did an excellent job writing this, shout-out to them. I'm now able to demystifyHaskell
, bit by bit, while having fun working on chapter exercises as the authors made me think, connect the dots and perform diagram chasing.
-
Functional programming is a function
- of data transformation, in a pure context
- composition
- that is lazy
-
Function, by default, is curried meaning it only accepts 1-argument and returns either
-
the reduced value
-
OR another function that will be further applied, and so on, which will eventually reduced into a value
-
-
Functions, when needed, are evaluated and eventually reduced into a value called Beta reduction
-
Functions are data because when needed they will eventually get evaluated and reduced into a value.
-
Tools for Building Haskell Projects/Packages
-
Use these resources
-
Always come back to this pattern
f x = y
when you're lost-
on where to apply the function to a value
-
or how many times to
lift
the function over multi-layered stack ofmonads
.- i.e.
ComposeType
,outerInner
and Scotty's Web - ActionT
- i.e.
-
more importantly, Haskell is based on principles from Combinatory Logic and Lambda Calculus wherein the idea of a function when combined with another function can represent Data Types such as Boolean and Numbers, Arithmetic, Logic, Data Structures such as Pair and List, and Recursion aka Y-Combinator)
-
newtype ComposeType f g h a = ComposeType { getComposeType :: f (g (h a)) } deriving (Eq, Show)
outerInner :: MaybeT (ExceptT String (ReaderT String (StateT String IO))) Int
newtype ActionT e m a =
ActionT
{ runAM
:: ExceptT
(ActionError e)
(ReaderT ActionEnv
(StateT ScottyResponse m))
a
}
deriving ( Functor, Applicative, MonadIO )
- When transforming a structure, NOT the value inside it, such as List-to-Maybe, use Natural Transformation i.e.
{-# LANGUAGE RankNTypes #-}
type Nat f g = forall a . f a -> g a
- Lifting the
function
to the base/outermostIO
monad via liftIO or lift, lift, lift
Haskell λ > :t liftIO
liftIO :: MonadIO m => IO a -> m a
Haskell λ > :t lift
lift :: (Monad m, MonadTrans t) => m a -> t m a
-
Use
:t
,:k
,:i
for details -
Use language pragma
{-# LANGUAGE InstanceSigs #-}
to have a clear vision of type signatures. i.e.
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
instance (Functor m) => Functor (MaybeT m) where
fmap :: (a -> b) -> MaybeT m a -> MaybeT m b
instance (Applicative m) => Applicative (MaybeT m) where
pure :: Applicative m => a -> MaybeT m a
(<*>) :: MaybeT m (a -> b) -> MaybeT m a -> MaybeT m b
instance (Monad m) => Monad (MaybeT m) where
(>>=) :: MaybeT m a -> (a -> MaybeT m b) -> MaybeT m b
instance (Foldable m) => Foldable (MaybeT m) where
foldMap :: (Monoid mn, Foldable m) => (a -> mn) -> MaybeT m a -> mn
instance (Traversable m) => Traversable (MaybeT m) where
traverse :: Applicative fa => (a -> fa b) -> MaybeT m a -> fa (MaybeT m b)
- Finally, after months of reading, more-and-more readings, lots of trial-and-error in
GHCi
, and countless nights/weekends working through chapter exercises, I can now celebrate completing this massive technical book. I don't consider myself reaching the ivory tower nor achieved some programming enlightenment but rather this experience gave me substantial knowledge to start the next chapter of my journey towards working in real-worldHaskell
applications.
- My background is primarily OOP, and have a very good understanding of FP working in Scala and ES6 high order functions but this chapter provides a good foundational details on lambda calculus
Haskell λ > let addAndMultiply = \x -> \y -> \z -> x + y * z
Haskell λ > addAndMultiply 3 5 7
38
- Introduction to the following
Haskell
subjects- Function and evaluation
- Infix operators
- Associativity and precedence
- Expression components such as
let
andwhere
- Printing strings
- Concatenation functions
- Built-in data types
- Integral vs Int, Integer and Word
- Bool and comparing values
- Tuples and Lists
- In OOP, such as Java, this is similar to defining
interface
.Haskell
data types doesn't have any function associated with it unlike Java wherein functions are automatically inherited fromjava.lang.Object
.- i.e. java.lang.Object class has toString, equals, hashCode etc.
- Type classes Eq, Num, Ord, Enum, Show, Read
- Type class inheritance
- Bottom
- Factorial! and Fibonacci numbers
- Pattern matching on lists
- List comprehensions
- Spines and non-strict evaluation
- Zipping lists
- Recursive patterns
- foldr vs foldl
- This chapter helped a lot as it preps me for Chapter 20 - Foldable and Chapter 21 - Traversable
- Algebraic datatypes
- Data and type constructors
- Type constructors and kinds
- Data constructors and values
- Data constructor arities
- newtype, Sum types, Product types
- Function type is exponential
- Higher-kinded data types
- Nothing, or Just Maybe
- Either left or right, but not both
- higher-kindedness
- Modules
- Making packages with Stack
- Building a project
- QuickCheck
- Arbitrary instances
-
Algebra and Laws
-
use of
newtype
-
To signal intent: using newtype makes it clear that you only intend for it to be a wrapper for the underlying type. The newtype cannot eventually grow into a more complicated sum or product type, while a normal datatype can
-
To improve type safety: avoid mixing up many values of the same representation, such as Text or Integer
-
To add different type class instances to a type that is otherwise unchanged representationally, such as with Sum and Product
-
Haskell λ > import Data.Monoid
Haskell λ > Sum 3 <> Sum 5 <> Sum 7
Sum {getSum = 15}
Haskell λ > Product 3 <> Product 5 <> Product 7
Product {getProduct = 105}
-
Laws
-
IO Functor
-
Functors are unique to a datatype
-
Functor is a way to apply a function over or around some structure that we don’t want to alter. That is, we want to apply the function to the value that is “inside” some structure and leave the structure alone.
Haskell λ > newtype MyName = MyName { getMyName :: String } deriving (Eq, Show)
Haskell λ > MyName <$> Just "gio"
Just (MyName {getMyName = "gio"})
Haskell λ > fmap (const 3) [Just "hello world"]
[3]
Haskell λ > (fmap . fmap) (const 3) [Just "hello world"]
[Just 3]
Haskell λ > (fmap . fmap . fmap) (const 3) [Just "hello world"]
[Just [3,3,3,3,3,3,3,3,3,3,3]]
-
Laws
-
Functor vs Applicative
-
ZipList Monoid
-
Applicatives are monoidal functors. The Applicative type class allows for function application lifted over structure (like Functor). But with Applicative the function we’re applying is also embedded in some structure. Because the function and the value it’s being applied to both have structure, we have to smash those structures together. So, Applicative involves monoids, like
mappend
, and functors.
Haskell λ > (*) <$> Just 3 <*> Just 5
Just 15
Haskell λ > Just (*3) <*> Just 5
Just 15
-
Laws
-
do
syntax and monads -
Application and composition
-
Think of
Monads
as another way of applying functions over structure, with the ability of the function to alter the structure, something we’ve not seen inFunctor
andApplicative
.Monad
can inject more structure. However, it has the ability to flatten those two layers of structure into one is what makesMonad
special. And it’s by putting thatjoin
function together with the mapping function that we getbind
, also known as>>=
. -
The
Monad
type class is essentially a generalized structure manipulation with some laws to make it sensible. Just likeFunctor
andApplicative
. -
Sample
Monad
that usesjoin
implicitly to flatten the structure within structure
Haskell λ > getLine >>= putStrLn
hello world
hello world
Haskell λ > :t getLine >>= putStrLn
getLine >>= putStrLn :: IO ()
- As oppose to a
Functor
Haskell λ > putStrLn <$> getLine
hello world
Haskell λ > :t putStrLn <$> getLine
putStrLn <$> getLine :: IO (IO ())
- To turn the
Functor
into aMonad
, use thejoin
explicitly to flatten the structure within structure
Haskell λ > import Control.Monad
Haskell λ > join $ putStrLn <$> getLine
hello world
hello world
Haskell λ > :t join $ putStrLn <$> getLine
join $ putStrLn <$> getLine :: IO ()
- Monoid
- Functor
- Applicative
- Monad
-
Revenge of the monoids
-
Foldable is a type class of data structures that can be folded to a summary value.
Haskell λ > import Data.Functor.Constant
Haskell λ > foldr (*) 3 (Constant 5)
3
Haskell λ > foldr (*) 1 [1,2,3,4,5]
120
-
Laws
-
traverse, sequenceA
-
Traversable allows you to transform elements inside the structure like a
Functor
, producingApplicative
effects along the way, and lift those potentially multiple instances ofApplicative
structure outside of the traversable structure. It is commonly described as a way to traverse a data structure, mapping a function inside a structure while accumulating the applicative contexts along the way. -
traverse
is mapping a function over some embedded value(s), likefmap
, but similar toflip bind
, that function is itself generating more structure. However, unlikeflip bind
, that structure can be of a different type than the structure we lifted over to apply the function. And at the end, it will flip the two structures around, as sequenceA did.
Haskell λ > import Data.Functor.Identity
Haskell λ > traverse (Identity . (+1)) [1, 2, 3]
Identity [2,3,4]
Haskell λ > runIdentity $ traverse (Identity . (+1)) [1, 2, 3]
[2,3,4]
sequenceA
is flipping two contexts or structures. It doesn’t by itself allow you to apply any function to the a value inside the structure; it only flips the layers of structure around.
Haskell λ > sequenceA [Just 3, Just 2, Just 1]
Just [3,2,1]
On the SkiFreeExercises, I'm able to write theFunctor
,Applicative
,Foldable
andTraversable
instance ofS
structure, wherefa
is also a functorial structure. TheMonad
instance, however, is failing on theright identity law
running thequickBatch
check from quickCheck. I still have to figure out how-to fix this correctly.
data S fa a = S (fa a) a deriving (Eq, Show)
-- TODO: fix the implementation of `monad` S
instance (Monad fa) => Monad (S fa) where
return = pure
-- (S fa a) >>= f = S (fa >>= f) (f a)
(S fa a) >>= f =
let S fa' a' = f a
in S fa' a'
monad laws:
left identity: +++ OK, passed 500 tests.
right identity: *** Failed! Falsifiable (after 1 test): S [] 0
associativity: +++ OK, passed 500 tests.
-
After finishing Chapter 25 - Composing types, it is NOT possible to compose
monad
but rather create amonad transformer
.- I've updated SkiFreeExercises
implementing
ST monad transformer
but still figuring out how to makeST
monadic structure intoS
structure. I'll come back to this once I'm finished with Chapter 26 - Monad Transfomers
- I've updated SkiFreeExercises
implementing
data S fa a = S (fa a) a deriving (Eq, Show)
newtype ST fa a = ST { runST :: fa a } deriving (Eq, Show)
-
Breaking down the Functor of functions
-
Functions have an Applicative too
-
Monad of functions
-
Reader is a way of stringing functions together when all those functions are awaiting one input from a shared environment. The important intuition is that it’s another way of abstracting out function application and gives us a way to do computation in terms of an argument that hasn’t been supplied yet. We use this most often when we have a constant value that we will obtain from somewhere outside our program that will be an argument to a whole bunch of functions. Using
Reader
allows us to avoid passing that argument around explicitly.
Haskell λ > (+) <$> (+3) <*> (*5) $ 7
45
Haskell λ > (+) <$> (runReader $ Reader (+3)) <*> (runReader $ Reader (*5)) $ 7
45
-
State
newtype -
Random numbers
-
The
State
type in Haskell is a means of expressing state that may change in the course of evaluating code without resort to mutation. The monadic interface for State is more of a convenience than a strict necessity for working with State.
Haskell λ > import Control.Monad.State
Haskell λ > (runState $ get >> put 5 >> return 9 >> modify (+3) >> return 12 >> modify (*5) >> return 9001) 3
(9001,40)
-
Parser
-
Parser combinator
-
Haskell’s parsing ecosystem
-
Marshalling from an AST to a datatype
-
The exercises on this chapter has a very substantial resources that really jogged my brain.
-
Composing types
-
Essentially, a structure that has multi-layered structures in it
-
Functors
andApplicatives
are both closed under composition, which means we can compose twofunctors
(or twoapplicatives
) and return anotherfunctor
(orapplicative
, as the case may be). This is not true ofmonads
, however; when we compose twomonads
, the result isNOT
necessarily anothermonad
.
-
newtype ComposeType f g h a = ComposeType { getComposeType :: f (g (h a)) } deriving (Eq, Show)
-
Monad Transformers
- Because we can't compose two
monads
together and create a newMonad
, we needMonad Transformer
to reduce the polymorphism and get concrete information about one of themonads
that we’re working with, to make thejoin
happen
- Because we can't compose two
-
IdentityT
-
Good chapter in preparation for
Monad Transformer
.
-
MaybeT, EitherT/ExceptT, ReaderT, StateT
-
Whoa!!! This chapter is loaded with great materials. Great job by the authors explaining and going thru it step-by-step.
-
Laziness, Non-Strict
-
Thunk, Sprint
-
Forcing Sharing
-
Bang Patterns
-
Strict and StrictData
-
Benchmarking with criterion
-
Map, Set, Sequence, Vector, String types
-
MVar
-
IO’s Functor, Applicative, and Monad
- Exceptions, try/catch, throw/throwIO, custom exception, async exception
- wreq - An easy-to-use HTTP client library
- scotty - Haskell web framework inspired by Ruby's Sinatra, using WAI and Warp
- hspec - A Testing Framework for Haskell
- checkers - Check properties on standard classes and data structures
- pandoc - Conversion between markup formats
- aeson - Fast JSON parsing and encoding
- cassava - A CSV parsing and encoding library
- parsers - Parsing combinators
- trifecta - modern parser combinator library with convenient diagnostics
- raw-strings-qq - Raw string literals for Haskell
- criterion - Robust, reliable performance measurement and analysis
- containers - Assorted concrete container types
- vector - Efficient Arrays
- array - Mutable and immutable arrays
- text - An efficient packed Unicode text type
- dlist - Difference lists
- twitter-conduit - Twitter API package with conduit interface and Streaming API support
- http-client - An HTTP client engine
- network - Low-level networking interface
- sqlite-simple - Mid-Level SQLite client library
- optparse-applicative - Utilities and combinators for parsing command line options
- Why Functional Programming Matters - by John Hughes
- What I Wish I Knew When Learning Haskell - by Stephen Diehl
- Haskell for all - by Gabriel Gonzalez
- How to desugar Haskell code - by Gabriel Gonzalez
- What a Monad is not
- Real World Haskell - by Bryan O'Sullivan
- Glasgow Haskell Compiler User's Guide
- Write You a Haskell - by Stephen Diehl
- Haskell Basics - by Stephen Diehl
- School of Haskell
- School of Haskell - Starting with Haskell
- School of Haskell - To Infinity and Beyond
- School of Haskell - Simple Examples
- School of Haskell - Pick of the Week
- Typeclassopedia - by Brent Yorgey
- Introduction to Haskell - by Brent Yorgey
- Monday Morning Haskell
- Monad Transformers
- Lenses
- Free Monads
- HaskForce
- Atom Haskell ghc-mod
- Stack Install ghc-mod
- Stack install ghc-mod failure fix
- stylish-haskell - Haskell code prettifier
- hindent - Extensible Haskell pretty printer
- hlint - Source code suggestions
- haskell-docs - Program to find and display the docs and type of a name
- Version 8.0.2
- Use this version, with
ghc-mod v5.8.0.0
, so that HaskForce works as a plugin in IntelliJ
- Use this version, with
- User Guide
- Build command
- Versions and installations
- How to Script with Stack
- How to Play with Stack
- How to Build with Stack
$ stack exec -- ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.0.2
$ stack exec -- ghc-mod --version
ghc-mod version 5.8.0.0 compiled by GHC 8.0.2
$ stack install scotty
$ stack install pandoc
$ stack exec -- pandoc --version
pandoc.EXE 2.2.1
Compiled with pandoc-types 1.17.5.4, texmath 0.11.1.2, skylighting 0.7.5
Default user data directory: C:\Users\Gio\AppData\Roaming\pandoc
- Creating executable file (make sure there's a
Main
module)
$ cd "C:\Users\Gio\Documents\_haskell\haskell-programming\ch30-when-things-go-wrong"
$ stack ghc -- WritingWithException.hs -o WritingWithException.exe
[1 of 1] Compiling Main ( WritingWithException.hs, WritingWithException.o )
Linking WritingWithException.exe ...
$ ./WritingWithException.exe
wrote to file
- Running the script without creating an executable file. Can't create an executable without
Main
module.
$ cd "C:\Users\Gio\Documents\_haskell\haskell-programming\ch29-io"
$ stack VigenereCipherExercises.hs -e ALLY VigenereCipherExercises.hs VigenereCipherExercises.encrypt.log
$ stack VigenereCipherExercises.hs -d ALLY VigenereCipherExercises.encrypt.log VigenereCipherExercises.decrypt.log
$ stack ghc -- VigenereCipherExercises.hs -o VigenereCipherExercises.exe
<no location info>: error:
output was redirected with -o, but no output will be generated
because there is no Main module.
- path
- env
- repl
- ghc/runghc
- script interpreter
- useful features
- power user commands
- unpack - to download package source
$ stack path
$ stack exec env
$ stack ghci
$ stack unpack scotty