- 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
IntvsInteger: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)
arising from a use of ‘maxBound’
• In the expression: maxBound :: Integer
In an equation for ‘it’: it = maxBound :: IntegerBeyonds 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)
atom = satisfy isUpper& (reverse function application)
- From
Data.Function. (&) :: a -> (a -> b) -> bf acan be used likea & f
λ> import Data.Function
λ> id 3
3
λ> 3 & id
3Symbols
<$> |
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 TrueAlso 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 <$> parsermeans that you need to parse something and then applyfto the result.
a <$ parsermeans 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 FalseInfix
- 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.ProxyPrelude 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
RULESpragma - Single pragma can have zero or more rules
- Multiple
RULESpragma 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 -Oimplies-fenable-rewrite-rules
- Variables used must either be in scope or quantified with a
forall- Pattern variables:
forallquantified 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
RULESoptimization 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
foo v = repeat Falsebut without KnownNat. repeat needs KnownNat.
ie, aim is like this:
{-# LANGUAGE PartialTypeSignatures #-}
foo :: Vec _ Bool -> Vec _ Bool
foo v = (repeat False) :: (typeOf v)Exploit this fact: 'Functor doesn't need KnownNat' (thanks to Rowan Goemans).
foo :: Vec _ Bool -> Vec _ Bool
foo v = fmap (const False) 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: recursionRec1forGeneric1: 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.projectfile is needed if there are multiple sub-packages within the packages, each with a distinct.cabalfile
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]
f xs = ys ++ ys
where ys :: [a]
ys = reverse xsThis is because the a in f and the one in ys are not considered the same. This function is same as:
f :: [a] -> [a]
f xs = ys ++ ys
where ys :: [b]
ys = reverse xsMeaning can be made clear to compiler by having a universal quantification over a.
{-# LANGUAGE ScopedTypeVariables #-}
f :: forall a. [a] -> [a]
f xs = ys ++ ys
where ys :: [a]
ys = reverse xsWith 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]
f (xs :: [aa]) = ys ++ ys
where ys :: [aa]
ys = reverse xsTools
- ormolu: automatic code formatter