Haskell Learners' Group (Session 3)
By Walker Malling and Richard Cook, 2018/6/6

Minutes of discussion for our third learners’ group session


… and therein endeth the preamble.

Topic of the day

Richard opened the pedagogical topic for the day: types and algebraic data types. Last time we looked at functions:

We can use :info to investigate symbols including types:

:info Int

This outputs:

data Int = GHC.Types.I# GHC.Prim.Int# 	-- Defined in ‘GHC.Types’
instance Eq Int -- Defined in ‘GHC.Classes’
instance Ord Int -- Defined in ‘GHC.Classes’
instance Show Int -- Defined in ‘GHC.Show’
instance Read Int -- Defined in ‘GHC.Read’
instance Enum Int -- Defined in ‘GHC.Enum’
instance Num Int -- Defined in ‘GHC.Num’
instance Real Int -- Defined in ‘GHC.Real’
instance Bounded Int -- Defined in ‘GHC.Enum’
instance Integral Int -- Defined in ‘GHC.Real’

Looking at the readout of :info on Int, Richard embarked on a tangent on Haskell’s typographical conventions:

Why does Haskell use uppercase letters for the names of types? Most likely, the designers of Haskell chose to do this to eliminate ambiguities in the parser.

Note that special characters like _ are often used to mark the monadic variant of a function, like for_, however you see other conventions like mapM or with “prime” (') (though this is also used to denote a strictly evaluated function such as foldl').

Another typographic peculiarity: infix and prefix functions. For example, we can define an infix function &&&&& where

> x &&&&& y = x + y
> 10 &&&&& 20
> 30

With an infix function like this, you can use () to use it as a prefix function:

> (&&&&&) 10 20
> 30

Conversely, you can use backticks ` to turn an prefix function into an infix function:

> &&&&& x y = x + y
> 10 `&&&&&` 20
> 30

Why would you do either? It may be useful to think about the grammar of the operation: consider if the function fits a subject-verb-object format; then the infix syntax makes sense.

x = map (+) [1, 2, 3]

:t x

x :: Num a => [a -> a]

map (\f -> f 10) x

[11, 12, 13]

Cue continuing typographical discussion regarding the capitalization of type names: maybe one reason, by contrast, is that a C++ parser must build up knowledge of whether something is a value or a type, and will take a different parsing path depending on what it encounters; this is also one reason why you have to declare functions and types before you can use them in C++. Haskell makes a typographical difference between types and values to avoid this requirement.

In GHC 2020: they plan to turn some language extensions on by default (perhaps OverloadedStrings, for example), especially the “uncontroversial” ones.

Returning to the topic of types:

You can use an alias for a type:

type Foo = Int

Just so, a String is an alias for [Char] most likely defined as follows:

GHCi will tell you this:

:i String
type String = [Char]

Note the difference between data and type keywords.

Richard gave several examples of the data keyword and mentioned data constructors, including recursive data constructors: :i [] and :i Maybe, and :i Either.

We investigated the type of Either

let x = Left "hello"
:t x
x :: Either [Char] b

Note that it is still polymorphic; b is not concrete and still represents a type variable.

data A = B Int
:t B
B :: Int -> A

Note: we’ll cover pattern matching next session.

Richard introduced the term “cardinality” of types (roughly, the “size” of the type).

Danielle asked: What is a sum type?

Richard addressed sum vs. product types.

A type can be a product or a sum of other types.(Examples in more detail next time.

In closing

At closing, Richard also mentioned Benjamin Pierce.