- Haskell home page : https://www.haskell.org/
- API search : https://www.haskell.org/hoogle/
- 5 minute haskell introduction : https://www.tryhaskell.org/
- Try Haskell online : https://glot.io/new/haskell , https://repl.it/languages/haskell
- Online Repl : https://repl.it/languages/haskell
- Style guide : https://github.com/tibbe/haskell-style-guide/blob/master/haskell-style.md
- Code Formatter : https://github.com/commercialhaskell/hindent
- Code linter : https://github.com/ndmitchell/hlint
- All in 1 platform : https://docs.haskellstack.org
- Written literally
- All Integers are of type
Num
when written literally Num
s can be coerced into any Integer or Float type- All Floats are of type
Fractional
when written literally Fractional
s cannot be coerced to Integrs
> :t 1
1 :: Num t => t
> :t 1.5
1.5 :: Fractional t => t
> 10 == (10 :: Integer)
True
> 10 == 10.0
True
> 10.0 == (10 :: Integer)
error
- Written literally as a series of didgits
Int
for machine integersInteger
for arbitrary precision- Use
Integer
unless you know you needInt
> a = 10 :: Integer
> b = 3 :: Integer
> a / b -- Connot use / to divide integers
error
> div a b -- div for integer division
3
> rem a b -- rem is modulus
1
> div a (3 :: Int) -- Cannot mix int types
error
> :t fromIntegral a -- Convert an Int(egral) type to a Num type
fromIntegral a :: Num b => b
- Implementations vary, guaranteed to be at least 30 bits.
> 9223372036854775807 :: Int
=> 9223372036854775807
> 9223372036854775808 :: Int
<interactive>:81:1: warning: [-Woverflowed-literals]
Literal 9223372036854775808 is out of the Int range -9223372036854775808..9223372036854775807
- Can hold values up to the memory limit of your machine
> 9223372036854775808 :: Integer
=> 9223372036854775808
Double
for Double-precision floating point.Float
for Single-precision floating point. Often used when interfacing with C.- Use
Double
unless you know you needFloat
- TODO Encoding
-
A List of Characters
-
TODO overloading strings
-
There is no string interpolation.
-
But formatting is built into the standard library http://hackage.haskell.org/package/base-4.2.0.1/docs/Text-Printf.html
> let a = [1,2,3,4]
> Text.Printf.printf "the length of %s is %d\n" (show a) (length a)
> elem 1 [2,3,4]
False
> elem 1 [1,2,3]
True
- Creates merely an
alias
for an existing type. - Can make code more readable.
- Can be used interchangeably with what is being aliased. Compiler don't care.
-- ┌ Age is simply an alias for Int, they can be used interchangeably
-- ⇣
type Age = Int
From the standard lib:
type String = [Char]
-- "String" is an alias for "List of Characters"
- Creates a
brand new
type. - It is common to name the
Value Constructor
the same as theType Name
-- ┌ Type Name
-- ⇣
newtype Age =
Age Int
-- ↑ └ What you must pass in to construct this type
-- └ Value Constructor
-- A function called "Age" that takes one "Int" now exists and it produces the value "Age some-int"
-- which has a type of "Age"
- Can take multiple paramaters
newtype Coordinates =
Coordinates Int Int
-- ↑ └ Paramater 2
-- └ Paramater 1
-- the function "Coordinates" now requires 2 paramaters an "Int" and another "Int" in
-- order to produce the value "Coordinates some-int some-int" of type "Coordinates"
- Simply a Type with multiple
Value Constructors
- It is common to name the
Value Constructors
differently than theType Name
-- ┌ Type Name
-- ⇣
data Pet
= Hamster Age
| Fish Age
-- ↑
-- └ "Tag" -or- "Type constructor"
-- Now the functions "Hamster" and "Fish" exist and they produce a value with the type of "Pet"
From the standard lib:
data Bool
= True
| False
- Simply a type who's value is a composite of many fields.
data Person = Person
{ name :: String
, age :: Age
}
- All of our types above are concrete -or- monomorphic.
- To make a type definition polymorphic simply add a variable.
-- ┌ Type Variable
-- ⇣
data Maybe a
= Nothing
| Just a
-- ↑
-- └ The constructor "Just" can be passed a value of any type to produce a value of type "Maybe"
- Generalizes function application over containers
<$>
is the infix alias forfmap
a <$ b
is the same asconst a <$> b
- Since Functor requires a
kind
of* -> *
it must only work on 1 type variable - for
(a, b)
it works onb
and leavesa
unchanged. - for
Either a b
it works onb
and leavesa
unchanged.
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
> fmap ((*) 2) [1,2,3]
-- [2,4,6]
> (*) 2 <$> [1,2,3]
-- [2,4,6]
> fmap ((*) 2) (Just 10)
-- Just 20
> fmap ((*) 2) (Left 10)
-- Left 10
> fmap ((*) 2) (Right 10)
-- Right 20
> fmap ((*) 2) (10,10)
-- (10, 20)
> "Hello" <$ Just 10
-- Just "Hello"
> Nothing <$ [1,2,3]
-- [Nothing,Nothing,Nothing]
- Generalizes how a strucutre should be associated with another structure of the same type
- Must have an identity function
mempty
class Monoid a where
mempty :: a
mappend :: a -> a -> a
mconcat :: [a] -> a
> mempty
-- ()
> mempty :: [a]
-- []
> mappend [1,2,3] [4,5,6]
-- [1,2,3,4,5,6]
> mconcat [[1,2,3], [4,5], [6]]
-- [1,2,3,4,5,6]
Sometimes there isn't one clear way to associate structures of the same type and so you must be more specific.
This is the case with Integral
and you must use the more specific Sum Integral
and Product Integral
types.
> import Data.Monoid
> mempty :: Sum Int
-- Sum 0
> mappend (Sum 9) mempty
-- Sum 9
> mconcat [(Sum 3), (Sum 4)]
Sum 7
> mempty :: Product Int
-- Product 1
> mappend (Product 9) mempty
-- Product 9
> mconcat [(Product 3), (Product 4)]
- Simply a monoid without an identity function
-- you must import Data.Semigroup to use
class Semigroup a where
(Data.Semigroup.<>) :: a -> a -> a
default (Data.Semigroup.<>) :: Monoid a => a -> a -> a
sconcat :: Data.List.NonEmpty.NonEmpty a -> a
stimes :: Integral b => b -> a -> a
- A way of generalizing a container of functions over a container of values
class Functor f => Applicative (f :: * -> *) where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
GHC.Base.liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
{-# MINIMAL pure, ((<*>) | liftA2) #-}
> pure 1 :: [Int]
-- [1]
> pure 1 :: Maybe Int
-- Just 1
> Nothing <*> Just 2
-- Nothing
> Just ((*) 4) <*> Nothing
-- Nothing
> Just ((*) 4) <*> Just 2
-- Just 8
> [minimum, maximum] <*> [[2,3,4], [5,6,7]]
-- [2,5,4,7]
> [1..10] *> [0,1]
-- [0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]
> [1..10] <* [0,1]
-- [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10]
> liftA2 (,) [1, 2] [3, 4] -- the same as (,) <$> [1,2] <*> [3,4]
-- [(1,3),(1,4),(2,3),(2,4)]
- A way of chaining together wrapped operations that can fail
class Applicative m => Monad (m :: * -> *) where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
{-# MINIMAL (>>=) #-}
m >>= return
= m
return x >>= f
= fx
- We define a typeclass with the
class
keyword.
Defining our own polymorphic equality (==) function:
class MyEq a where
equal :: a -> a -> Bool
notEqual :: a -> a -> Bool
notEqual x y = not (equal x y)
- We define implementations of the typeclass with the
instance
keyword - Since
notEqual
has a default implementation that depends onequal
we only have to specifyequal
Implementing equal
for Bool
:
instance MyEq Bool where
equal True True = True
equal False False = True
equal _ _ = False
(.)
: Function Composition
> :t (.)
-- (.) :: (b -> c) -> (a -> b) -> a -> c
> a = ((*) 2) . const 4
> a 123
-- 8
> a Nothing
-- 8
($)
: Pipe Left
> :t ($)
-- ($) :: (a -> b) -> a -> b
> head $ map (*2) [13, 50, 1000]
-- 26
- The first guard that evaluates to
true
is run. otherwise == true
- If no guards match and there are no other function heads, you will get
Exception: Non-exhaustive patterns in function myFun
myFilt :: (a -> Bool) -> [a] -> [a]
myFilt _ [] = []
myFilt f (x:xs)
| f x = x : myFilt f xs
| otherwise = myFilt f xs
> myFilt even [1..10]
[2,4,6,8,10]
isFour :: (Eq a, Num a) => a -> Bool
isFour a
| a == 4 = True
> isFour 4
True
> isFour 5
*** Exception: Non-exhaustive patterns in function isFour
> [ x ^ 2 | x <- [1..10]]
[1,4,9,16,25,36,49,64,81,100]
> [(x, y) | x <- [1..10], y <- [1..10], x > 7, y < 3]
[(8,1),(8,2),(9,1),(9,2),(10,1),(10,2)]
-- Remember, Strings are Lists
> [ x | x <- "This is Haskell", elem x ['A'..'Z']]
"TH"
as | case | class | data | default | deriving | do | else |
family | forall | foreign | hiding | if | import | in | infix |
infixl | infixr | instance | let | mdo | module | newtype | of |
proc | qualified | rec | then | type | where |
` | ' | " | - | -- | -< | -<< | -> |
:: | ; | <- | , | = | => | > | ~ |
! | ? | # | * | @ | [|, |] | \ |
_ |
{, } | {-, -} | | |
- Enabled per file with
{-# LANGUAGE QuasiQuotes #-}
- Enabled on the compiler with
-XQuasiQuotes
- Let you use/implement a DSL to write haskell
- Follows format of
[functionName| some-content |]
- Are context aware so you can include bindings in scope
Truncated example for producing html in haskell with Yesod:
{-# LANGUAGE QuasiQuotes #-}
import Text.Hamlet (shamlet)
-- ┌ Quasi Quoter called shamlet
-- ⇣
main = putStrLn $ renderHtml [shamlet|
<p>Hello, my name is #{name person} and I am
<strong> #{show $ age person}.
|]
-- └ Close the quasi quotes
where
name = "Michael"
age = 26
-- ↑
-- └ Bindings
- Start by running
ghci
- Start the repl by running
stack ghci
orstack repl
- Specify extra packages to include in the repl context with:
stack ghci --package regex-posix
- and then import them with
:m +Text.Regex.Posix
:help
-or-:h
: Print a list of repl commands:type some-value
-or-:t some-value
: Display type information for a value:load some-module
-or-:l some-module
: Load a module into the repl:reload
-or-:r
: Reload all loaded modules:module
-or-:m
: Unload all loaded modules
> :t Nothing
Nothing :: Maybe a
> :t (round 1.5)
(round 1.5) :: Integral b => b
- delimited with
:{
and:}
> :{
> some_multi_line
> expression
> :}
- Literate Haskell : https://wiki.haskell.org/Literate_programming