- 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
Numwhen written literally Nums can be coerced into any Integer or Float type- All Floats are of type
Fractionalwhen written literally Fractionals 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
Intfor machine integersIntegerfor arbitrary precision- Use
Integerunless 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
=> 9223372036854775808Doublefor Double-precision floating point.Floatfor Single-precision floating point. Often used when interfacing with C.- Use
Doubleunless 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
aliasfor 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 = IntFrom the standard lib:
type String = [Char]
-- "String" is an alias for "List of Characters"- Creates a
brand newtype. - It is common to name the
Value Constructorthe 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 Constructorsdifferently 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 forfmapa <$ bis the same asconst a <$> b- Since Functor requires a
kindof* -> *it must only work on 1 type variable - for
(a, b)it works onband leavesaunchanged. - for
Either a bit works onband leavesaunchanged.
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
classkeyword.
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
instancekeyword - Since
notEqualhas a default implementation that depends onequalwe 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
trueis 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 ghciorstack 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