- Haskell 2010 report: https://www.haskell.org/definition/haskell2010.pdf
- A history of haskell 2007: https://dl.acm.org/doi/pdf/10.1145/1238844.1238856
General
- Monad transformers: Takes a monad as argument to return another monad
- Getting rid of quotes in
show char
: For ac :: Char
, use[c] :: String
ˡ - If the minimal type class definition is 'Nothing' in the docs, it means that even if no definitions are given, an instance is derived
Predefined types
Int
vsInteger
:Int
: fixed precisionInteger
: arbitrary precision
Word
: 'an unsigned integral type, with the same size as Int' - Haskell language report 2010 ˡ
Bounds of a Int
Bounds can be seen for instances of Bounded
type class:
> minBound :: Int
λ-9223372036854775808
> maxBound :: Int
λ9223372036854775807
> maxBound :: Bool
λTrue
> minBound :: Bool
λFalse
> maxBound :: Word
λ18446744073709551615
> maxBound :: Float
λ<interactive>:5:1: error:
No instance for (Bounded Float) arising from a use of ‘maxBound’
• In the expression: maxBound :: Float
• In an equation for ‘it’: it = maxBound :: Float
> maxBound :: Integer
λ<interactive>:6:1: error:
No instance for (Bounded Integer)
• of ‘maxBound’
arising from a use In the expression: maxBound :: Integer
• In an equation for ‘it’: it = maxBound :: Integer
Beyonds bounds, value will wrap around:
λ> (maxBound :: Int) + 1
-9223372036854775808
Tilde in type
~
showing up in a type annoation indicates that two types are equivalent. It is a type constraint that haskell would need to be able to solve inorder for the code to type check.
Eg (from a use of megaparsec):
atom :: (Token s ~ Char, MonadParsec e s m) => m (Token s)
= satisfy isUpper atom
&
(reverse function application)
- From
Data.Function
. (&) :: a -> (a -> b) -> b
f a
can be used likea & f
> import Data.Function
λ> id 3
λ3
> 3 & id
λ3
Symbols
<$> |
fmap |
Functor f => (a -> b) -> f a -> f b |
<*> |
(apply) | Applicative f => f (a -> b) -> f a -> f b |
>>= |
(bind) | Monad m => m a -> (a -> m b) -> m b |
<$ |
Functor f => a -> f b -> f a |
|
<│> |
(alternative) | Alternative f => f a -> f a -> f b |
<* |
Applicative f => f a -> f b -> f a |
|
*> |
Applicative f => f a -> f b -> f b |
|
>> |
?? | Monad m => m a -> m b -> m b |
>< |
?? | |
<> |
?? | Semigroup a => a -> a -> a |
. |
(compose) | (b -> c) -> (a -> b) -> (a -> c) |
$ |
(a -> b) -> a -> b |
(>>)
a >> b
is similar to a >>= b
except that the value produced from a
is ignored. ᵈ
Kind of like:
a >>= b |
a >> b |
---|---|
do | do |
x <- a | a |
b | b |
Similar to *>
as evident from type.
(<>)
Associative operator associated with a semigroup ᵈ ᵈ. ᵈ
In the case of list:
> [1,2] <> [3]
λ1,2,3] [
because list is an instance of Semigroup
:
λ> :i []
type [] :: * -> *
data [] a = [] | a : [a]
-- Defined in `GHC.Types'
instance Alternative [] -- Defined in `GHC.Base'
instance Applicative [] -- Defined in `GHC.Base'
instance Eq a => Eq [a] -- Defined in `GHC.Classes'
instance Functor [] -- Defined in `GHC.Base'
instance Monad [] -- Defined in `GHC.Base'
instance Monoid [a] -- Defined in `GHC.Base'
instance Ord a => Ord [a] -- Defined in `GHC.Classes'
instance Semigroup [a] -- Defined in `GHC.Base'
instance Show a => Show [a] -- Defined in `GHC.Show'
instance Foldable [] -- Defined in `Data.Foldable'
instance Read a => Read [a] -- Defined in `GHC.Read'
instance MonadFail [] -- Defined in `Control.Monad.Fail'
instance Traversable [] -- Defined in `Data.Traversable'
<$
Kinda like:
- Take the 'shape' of RHS and 'dress' up LHS in that shape.
> :t (<$)
λ(<$) :: Functor f => a -> f b -> f a
> (<$) 3 [4]
λ3]
[
> (<$) 3 [3]
λ3]
[
> (<$) True [False]
λTrue]
[
> (<$) True (Just False)
λJust True
Also see:
- https://stackoverflow.com/questions/14087881/what-is-the-purpose-of-in-the-functor-class
- https://stackoverflow.com/questions/38557100/how-does-function-work-in-the-functor-class
From the first link:
f <$> parser
means that you need to parse something and then applyf
to the result.
a <$ parser
means that you don't need to parse anything (you're not interested in the result) - you only need to recognize, which can be much faster.
.
- As in
g . f
≡g (f x)
(.) :: (b -> c) -> (a -> b) -> (a -> c)
- First apply arg2 then use its result on arg1.
- Computation sort of goes from right to left.
<*
and *>
, <<
<*
feels like 'ensure RHS is there but return LHS'*>
is the other direction.>>
: like*>
but for monads
> :t (<*)
λ(<*) :: Applicative f => f a -> f b -> f a
> (<*) (Just True) (Just False)
λJust True
> (*>) (Just True) (Just False)
λJust False
---
> :t (>>)
λ(>>) :: Monad m => m a -> m b -> m b
> (>>) (Just True) (Just False)
λJust False
Infix
- Constructors can be made infix too. Just start its name with a
:
. - Fixity means precedence
- 0 = lowest precedence
- 9 = highest precedence
- 10 = precedence of function application
- 11 = precedence of record syntax
Example:
data Re = Atom
| Re :- Re -- Concat
infixr 6 :-
- infixr: right associativity
- infixl: left associativity
- infix: no associativity
—
Precedence: Higher the number, higher the precednece.
Built-in stuff:
infixl 9 !!
infixr 9 .
infixr 8 ^
infixl 7 *
infix 7 /, `div`, `quot`, `rem`, `mod`
infixl 6 +, -
infix 5 \\
infixr 5 ++, :
infix 4 ==, /=, <, <=, >=, >
infix 4 `elem`, `notElem`
infixr 3 &&
infixr 2 ||
infixr 0 $
—
See:
- https://www.haskell.org/onlinereport/decls.html#fixity
- https://wiki.haskell.org/Constructor
- https://stackoverflow.com/questions/30039123/haskell-why-arent-infix-type-constructors-allowed
List comprehension
Similar to the one in Python.
> chars = ['a'..'e']
> [char | char <- chars]
"abcde"
Or with multiple lists like
> let chars = ['a'..'c']
> let nums = [1..4]
> [(char, num) | char <- chars, num <- nums]
[('a',1),('a',2),('a',3),('a',4),('b',1),('b',2),('b',3),('b',4),('c',1),(
'c',2),('c',3),('c',4)]
List operation functions
head lst
Get first element.
> head [1..3]
1
tail lst
Get everything except the first element.
> tail [1..3]
[2,3]
take n lst
Get ('takes') the first n
number of elements from the argument list
> take 2 ['a'..'e']
"ab"
drop n lst
Create a new list from lst
, dropping the first n
elements.
> drop 6 [1..10]
[7,8,9,10]
sum <lst>
Add all elements of the list.
> sum [1..3]
6
product <list>
Find the product of all elements of the list.
> product [1..3]
6
span <condition> <list>
span :: (a -> Bool) -> [a] -> ([a], [a])
{-
(a -> Bool) is the condition; which is in effect a function
returning a Bool.
-}
Splits input list into two parts.
Returns a tuple of two lists.
Takes elements from the list, and returns it as the first output list, as long as condition is true. Remaining elements, including the one for which the conditon became false, will be placed in the second output list.
Example:
> span (/='a') "helloaworld"
("hello","awo")
length <list>
length :: Foldable t => t a -> Int
Returns the length of argument list.
> length "hello"
5
> length [3,4]
2
Check presence of a value in the list
elem <element> <list>
Example:
> elem 'a' "ab"
True
> elem 'c' "ab"
False
Check absence of a value in the list
notElem <element> <list>
Opposite of Elem
.
> notElem 'a' "ab"
False
> notElem 'c' "ab"
True
Indexing (!!)
Indexing starts from 0
.
> [0,1,2,3] !! 2
2
> "abcdef" !! 1
'b'
> "abc"!!3
*** Exception: !!: index too large
Proxy
import Data.Proxy
Prelude Data.Proxy> :i Proxy
type role Proxy phantom
data Proxy (t :: k) = Proxy
Type role
https://downloads.haskell.org/~ghc/7.8.4/docs/html/users_guide/roles.html#:~:text=The%20role%20of%20a%20type,as%20T%20Int%20Bool%20c%20).
3 possible roles:
- nominal: must be same
- representational: must have same representation
- phantom: can be anything??
type role Set nominal
type role Ptr representational
type role
can be used by the compiler to ensure that no type promises are broken.
Example of use nominal
type role: https://downloads.haskell.org/~ghc/7.8.4/docs/html/users_guide/roles.html
As another example, we can consider a type Set a that represents a set of data, ordered according to a's Ord instance. While it wouldnnn generally be type-safe to consider a to beat role representational, it is possible that a newtype and its base type have different orderings encoded in their respective Ord instances. This would lead to misbehavior at runtime. So, the author of the Set datatype would like its parameter to be at role nominal. This would be done with a declaration
Role annotations can't be done for type synonyms (ie, those declared with type
). But is possible for newtype
I guess.
I guess:
- type roles are like 'role of a type argument in the type', not 'role of a type as a whole'
- we can talk about the type role only for the last type argument
- NO I THINK: 'role annotation declaration starts with type role and is followed by one role listing for each parameter of the type'. But yet to find example
- default type role is
phantom
- In terms of strictness: nominal > representational > phantom
--
type role T3 _ nominal
data T3 a b = MkT3 a
-- role of a is left for ghc to infer (by underscore)
-- b is phantom here. nominal was unnecessary. but it won't cause error. just unnecessarily strict??
From #haskell:
15:48 < int-e> Yes, this is about restricting coercions. To coerce T a to T b, if the argument has a nominal role, a and be must be the same; if it's representational, then a and b must be coercible, and if it's a phantom argument, then you can coerce no matter what the types a and b are.
Doubts:
- type role T4 nominal; data T4 a = MkT4 (a Int) – OK, but nominal is higher than necessary
- Why is representational fine here?
Equality constraints
https://downloads.haskell.org/~ghc/7.4.1/docs/html/users_guide/equality-constraints.html#:~:text=where%20we%20require%20that%20the,rank%20types%20are%20otherwise%20enabled
Syntax where you see stuff like (a ~ b)
Type coercions
type role Set nominal
, where Set
has kind * -> *
, prevents coercion of Set a
to Set b
. (Doubt: Even when a
and b
are same??)
Coercion is possible when the compiler is sure that the two types have the same representation. https://hackage.haskell.org/package/base-4.19.0.0/docs/Data-Coerce.html
See: Zero-cost coercions for Haskell - ICFP 2014 by Breitner, Eisenberg, Jones, Weirich https://www.seas.upenn.edu/~sweirich/papers/coercible-JFP.pdf
Apparently, some definitions related to those from Data.Coerce
: https://hackage.haskell.org/package/base-4.19.0.0/docs/Data-Type-Coercion.html
Data.Tagged
: Related to Proxy
type?
Bottom
- Written as
_|_
in haskell documentation (for⊥
) - A value that never finishes computing successfully. Either due to error, or because of infinite loop.
- Can be a value of any type
- https://wiki.haskell.org/Bottom
Pragmas
https://downloads.haskell.org/~ghc/7.0.3/docs/html/users_guide/pragmas.html
INLINE
,NOINLINE
: Control inlining of function definitions- Eg:
{-# INLINE myfun #-}
- Eg:
INLINABLE
: Suggest inlining while still leaving the final decision to the compiler.ANN
: Annotate
GHCOPTIONS
Looks like this is for passing options that could be passed down to ghc command.
Eg: Disable a specific warning throughout a file:
-- For 'warning: [-Wpartial-type-signatures]'
{-# OPTIONS_GHC -fno-warn-partial-type-signatures #-}
Rewrite rules
- Specified with
RULES
pragma - Single pragma can have zero or more rules
- Multiple
RULES
pragma use also possible
- Multiple
- 'the closing
#-}
should start in a column to the right of the opening{-#
' - Rules are enabled by
-fenable-rewrite-rules
(disabled by-fno-enable-rewrite-rules
)ghc -O
implies-fenable-rewrite-rules
- Variables used must either be in scope or quantified with a
forall
- Pattern variables:
forall
quantified variables
- Pattern variables:
Syntax:
rule-name [phase-control-number] rewrite-equation
Exampleˢ:
{-# RULES
"map/map" forall f g xs. map f (map g xs) = map (f.g) xs
#-}
- To use
RULES
optimization needs be active => can't be used from ghci ʳ
See:
Find size of a value
Use HeapSize
module.
λ> runHeapsize 30 $ recursiveSizeNF (1::Int)
Just 7
λ> runHeapsize 30 $ recursiveSizeNF True
Just 7
λ> runHeapsize 30 $ recursiveSizeNF (True, False)
Just 36
λ> runHeapsize 30 $ recursiveSizeNF (Left True :: Either Bool Bool)
Just 33
https://hackage.haskell.org/package/heapsize-0.3.0.1/docs/HeapSize.html
Making a Read
instance for a class
- https://archive.alvb.in/msc/03_infoafp/papers/2013-01-08_HoorCollege_HaskellDoYouReadMe_dk.pdf
- https://discourse.haskell.org/t/instance-read-a/4367/2
- https://stackoverflow.com/questions/14006707/making-a-read-instance-in-haskell
- http://zvon.org/other/haskell/Outputprelude/Read_c.html
- https://hackage.haskell.org/package/base-4.20.0.1/docs/Text-Read.html
ghci
http://dev.stephendiehl.com/hask/
For interactive haskell REPL.
Command | Short | Description |
---|---|---|
:reload | :r | Code reload |
:type | :t | Type inspection |
:kind | :k | Kind inspection |
:info | :i | Information (can show definiton with constructors) |
:p | Print the expression | |
:edit | :e | Load file in system editor |
:load | :l | Set the active Main module in the REPL |
:module | :m | Add modules to imports |
:add | :ad | Load a file into the REPL namespace |
:instances | :in | Show instances of a typeclass |
:browse | :bro | Browse all available symbols in the REPL namespace |
'List difference'
Needs Data.List
.
Prelude> import Data.List
Prelude Data.List> [1,2,3] \\ [2]
[1,3]
'Tricks'
Avoiding KnownNat
Suppose we need this:
foo :: KnownNat n => Vec n Bool -> Vec n Bool
= repeat False foo v
but without KnownNat
. repeat
needs KnownNat
.
ie, aim is like this:
{-# LANGUAGE PartialTypeSignatures #-}
foo :: Vec _ Bool -> Vec _ Bool
= (repeat False) :: (typeOf v) foo v
Exploit this fact: 'Functor doesn't need KnownNat
' (thanks to Rowan Goemans).
foo :: Vec _ Bool -> Vec _ Bool
= fmap (const False) v foo v
—
> (const False) <$> [0,1]
λFalse,False]
[
> fmap (fmap (const False)) [[0,1], [2,3]]
λFalse,False],[False,False]]
[[
> (const False <$>) <$> [[0,1], [2,3]]
λFalse,False],[False,False]] [[
From Haskell Language report 2010
The constructor ":" is reserved solely for list construction; like [], it is considered part of the language syntax, and cannot be hidden or redefined. It is a right-associative operator, with precedence level 5
Generic
https://hackage.haskell.org/package/base-4.21.0.0/docs/GHC-Generics.html
- Rep
- ~to
- Rep -> type~
- ~from
- type -> Rep~
U1
: for constructor without argsC1
: constructor:*:
: separate args of a constructor:+:
: separate constrs of a type (ie, sum type)L1
: leftR1
: left
Rec0
: recursionRec1
forGeneric1
: for recursive calls like* -> *
M1
: meta informationS1
: type synonym representing meta info of record selectorsD1
: type synonym representing meta info of typesK1
: constantsV1
: types without constructors
Misc
ghc hints: https://downloads.haskell.org/ghc/latest/docs/users_guide/hints.html
Cabal is a library, cabal-install uses it
cabal.project
file is needed if there are multiple sub-packages within the packages, each with a distinct.cabal
file
hedgehog: apparently better than quickcheck
Change ghcup installation location:
GHCUP_INSTALL_BASE_PREFIX=<path>
Tasty: for testing
- Needs separate backend, apparently. Hunit, quickcheck available.
Phantom types
Some humour: http://web.archive.org/web/20240705080317/www.willamette.edu/~fruehr/haskell/evolution.html
A subtlety
(From here.)
Following function won't type check.
f :: [a] -> [a]
= ys ++ ys
f xs where ys :: [a]
= reverse xs ys
This is because the a
in f
and the one in ys
are not considered the same. This function is same as:
f :: [a] -> [a]
= ys ++ ys
f xs where ys :: [b]
= reverse xs ys
Meaning can be made clear to compiler by having a universal quantification over a
.
{-# LANGUAGE ScopedTypeVariables #-}
f :: forall a. [a] -> [a]
= ys ++ ys
f xs where ys :: [a]
= reverse xs ys
With the forall
, the type variable bound by a
ranges throughout the entire defintion of f
. This way, haskell would know that the a
used in both locations are the same.
The same function could be written without forall
as well. Using a pattern type signature like thisʳ:
{-# LANGUAGE ScopedTypeVariables #-}
f :: [a] -> [a]
xs :: [aa]) = ys ++ ys
f (where ys :: [aa]
= reverse xs ys
Tools
- ormolu: automatic code formatter