Haskell


General

Predefined types

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 :: 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)
atom = satisfy isUpper

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:

λ> :t (<$)
(<$) :: Functor f => a -> f b -> f a

λ> (<$) 3 [4]
[3]

λ> (<$) 3 [3]
[3]

λ> (<$) True [False]
[True]

λ> (<$) True (Just False)
Just True

Also see:

From the first link:

f <$> parser means that you need to parse something and then apply f 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.

.

<* and *>, <<

λ> :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 :.

Example:

data Re = Atom 
        | Re :- Re  -- Concat

infixr 6 :-

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:

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:

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 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:

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

Pragmas

https://downloads.haskell.org/~ghc/7.0.3/docs/html/users_guide/pragmas.html

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

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
  #-}

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

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)
:print :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]

Third party stuff

Parsec

Containers

Seq (as in list) type from Data.Sequence of 'containers' package.

<│ Add element to left end of a seq a -> Seq a -> Seq a
│> Add element to right end of a seq a -> Seq a -> Seq a
>< Concatenate two sequnces Seq a -> Seq a -> Seq a

Bricks

Timers can be made with BChan

More:

Lens

https://hackage.haskell.org/package/lens-tutorial-1.0.5/docs/Control-Lens-Tutorial.html

%=:

>>> execState (do _1 %= f;_2 %= g) (a,b)
(f a, g b)

>>> execState (do both %= f) (a,b)
(f a, f b)

.=:

>>> execState (do _1 .= c;_2 %= d) (a,b)
(c, d)

>>> execState (do both %= c) (a,b)
(c, c)

More concepts:

Functions: re, unto, set, .=,


One possible use of lens: getters-setters when using nested record types

It's like this:

data Lens a b = Lens
{ view :: a -> b
, over :: (b -> b) -> (a -> a)
}

Create lens for a type with:

makeLenses ''<bigtype>
makeLenses ''<smalltype>

This is made possible with TemplateHaskell.

Field names in the record type should have underscore prefix.

'Tricks'

Avoiding KnownNat

Suppose we need this:

foo :: KnownNat n => Vec n Bool -> Vec n Bool
foo v = repeat False

but 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]]

Misc

A subtlety

(From here.)

Following function won't type check.

f :: [a] -> [a]
f xs = ys ++ ys
  where ys :: [a]
        ys = reverse xs

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]
f xs = ys ++ ys
  where ys :: [b]
        ys = reverse xs

Meaning 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 xs

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]
f (xs :: [aa]) = ys ++ ys
  where ys :: [aa]
        ys = reverse xs

Tools