Haskell – Fundamentals

I’m in the process of learning to program in Haskell in order to write smart contracts on the Cardano blockchain.

The purpose of this page is to be a quiz that you can go through from start to finish on a daily basis in order to help gain and retain your knowledge of Cardano. It’s a technique I’ve used when learning other things in the past though this is the first time I’m applying it to coding.

I’m doing it with Haskell, as the syntax is so different to most other languages that the repetition will help it to feel intuitive.

DISCLAIMER: This is written from the point of view from someone that started learning Haskell in the summer of 2021. I have tried to make sure everything I write is correct, but if you do see any misinformation, let me know.

Firstly some good resources:

Haskell Tutorial – Tutorialspoint (good for a quick understanding of concepts)

Learn You a Haskell for Great Good! (famous tutorial, pretty good)

What I Wish I Knew When Learning Haskell 2.5 ( Stephen Diehl )

Haskell Operator Cheat Cheat


Haskell Book | Haskell from the Very Beginning

Programming in Haskell Hutton


This section covers some basic knowledge before we get into the main Q&A. No need to go over them every day.

What’s the difference between module, library, package, and Hackage?

module is a set of functions, types, classes, … put together in a common namespace.

library is a set of modules which makes sense to be together and that can be used in a program or another library.

package is a unit of distribution that can contain a library or an executable or both. It’s a way to share your code with the community.

Hackage is an open source collection of Haskell packages


What does all of the following mean? Cabal, cabal, REPL, GHCi, GHC, Stack?

  • A REPL is bit like an interactive shell used in some languages. (See this if you want to get into the weeds of the difference)
  • Haskell’s REPL is called GHCi.
  • The alternative would be to write a Haskell script and compile it using Haskell’s native compiler, GHC
  • You can compile a .hs file in Haskell using GHC provided you include a module called main. e.g.
main :: IO ()
main = return ()
double :: Int -> Int
double n = 2*n
  • When building more complex code that utilizes other libraries you need to make use of a package manager called Cabal (upper case C) to compile your code
  • To run Cabal you use the command line interface called cabal (lower case c). Note: To avoid having to rebuild your code every time you can use a Cabal version of REPL through the cabal repl command. However I have not yet gotten my head around how to use this.
  • You learn more about Cabal here: Haskell – Cabal basic introduction
  • Stack is an alternative to the command line cabal. It also uses Cabal (upper case C) but it simplifies package management by maintaining its own fixed list of package versions that it knows work well together. Stack is seen as more reliable. Also see this.

As an aside, sometimes the interpreter prompt say “ghci” and sometimes it says “prelude”. They are the same Haskell library. I believe prelude got renamed to ghci. You may see either in my examples as I switch between the two.

How do you load a file into the Cabal REPL?

:l <filepath>/<filename.hs>

What do we mean by checking the type and how would you do it?

Checking the type means checking the input and outputs data type of any function or value. You use :t followed by the name of the function to view the type. The first three examples below are checking data type that a value belongs to, the last one is checking the data types for a function

prelude> :t "a"  
'a' :: Char  

prelude> :t True  
True :: Bool  

prelude> :t "HELLO!"  
"HELLO!" :: [Char] 

prelude :t head  
head :: [a] -> a 

Note that you can also is :i for a slightly more detailed set of info for certain things as see in the first and the third example below.

Prelude> :i "a" 
<interactive>:1:1: error: parse error on input ‘"’

Prelude> :i True
type Bool :: *
data Bool = ... | True
-- Defined in ‘ghc-prim-0.6.1:GHC.Types’

Prelude> :i "HELLO!"
<interactive>:1:1: error: parse error on input ‘"’

Prelude> :i head 
head :: [a] -> a 	
-- Defined in ‘GHC.List’

How do you change the command prompt?

Haskell tutorials tend to chanve the prompt from Prelude to ghci but never tell you they did that. Well here is how to change it:

Prelude>:set prompt ghci>


This section is good to test yourself on a daily basis when you first start to learn Haskell to get you to the point you can interpret basic code.

Why would this return an error if you typed in GHCi and how would you fix it?

double :: Int -> Int
double n = 2 * n

GHCi cannot handle function definitions that span more than one line like this (but GHC the compiler can).

Two ways of handling are, either write it all as one line with a semi-colon in between:

double :: Int -> Int;double n = 2 * n

Or enter :{ to tell GHCi that you wish to type in multiple lines, then end it by typing :}

Prelude> :{
Prelude| double:: Int -> Int
Prelude| double n = 3 * n
Prelude| :}

What does this code do?

addText :: [Char] -> [Char]
addText x = x ++ "test"

Ans: This is declaring a function call addText, that takes in an array of characters (i.e. a string) as input and then adds the word test onto the end.

Why is this wrong?

abs -6

Ans: Needs to ne abs (-6) as else it will end up doing abs (nothing) minus 6

What happens if you type in n=n+1 in Haskell

It gets caught in an infinite loop

What will this result in?

:t (True, 'a') 


(True, 'a') :: (Bool, Char)

What is this showing?

func2 :: Int -> Int -> Int
func2 x y = x+y 

Ans: This is declaring a function that takes in two integer inputs, and adds them together

What does the -> symbol mean or do?

A: It means “returns”. Generally you will see it when you look at the definition of a function. But you can also use it yourself when you are defining an anonymous function such as:

Write a simple function

Prelude> a = x -> 2*x
Prelude> a 1

Explain Maybe

The Maybe function is wrapper around other data types, allowing it to deal with null errors in cases where you don’t want to raise an exception error (i.e. cause the code to crash and exit). If a function has an error, the Maybe wrapper will bring back a Nothing data type, else it will bring back the result.

For some reason the designers of Haskell felt the need to include the word Just in front of the result, so one would say Maybe gives you back Just result or Nothing (you may hear it as Just a or nothing as well)

If you want to understand why we have Just in Haskell, read this and then give me a simple sentence or two I can use,

Maybe is used quite a bit because it effectively “lifts” or extends a type, such as Integer from your example, into a new context in which it has an extra value (Nothing) that represents a lack of value! The type system then requires that you check for that extra value before it will let you get at the Integer that might be there. This prevents a remarkable number of bugs.

Many languages today handle this sort of “no-value” value via NULL references. The Maybe type is not the only way to fix this, but it has proven to be an effective way to do it. [1][2]

Example of Maybe function:

data Maybe a = Nothing | Just a
safediv :: Int -> Int -> Maybe Int
safediv n m = 
if m == 0 then Nothing
 else Just (n `div` m)


Haskell for Imperative Programmers #14 – Maybe – YouTube

What does the “Just” syntax mean in Haskell? – Stack Overflow

What is a polymorphic type?

Haskell uses lower case letters to denote variables that can be any type of data. So for example instead of defining a function as

length :: [Int] -> Int

which would be the function definition for count the number of integers in a list of integers, it could be defined as

length :: [a] -> Int

this would be the definition for a function wishing to count a list of anything. For something to be a polymorphic type it must be a lower case letter.

What is overloading types?

Overloading is when you add in a type constraint to a function definition. For example:

(+) :: Num a => a -> a -> -> a

This means that the + function can take in any data type provided its numeric.


Write a named function and an anonymous (lambda) function for f(x) =x+1

f x = x+1      -- named function

-- or

f = x -> x+1  -- lambda function

What does map do?

Ans: map takes a function and a list and applies that function to every element in the list, producing a new list. Here is an example:

 map (x -> x*2+1) [1..10]

Note that there is a more generic version of map called fmap which can apply a function onto another function, not just a list. Apparently this was originally called ‘map’ in Haskell, and then they decided to create a dummied down version and call that map, and call the original one fmap.

Am explaining that map is just a dummied down version of fmap will be useful later on when learning about functors.

How would you re-write this in a Haskell way?

    function roots(a,b,c) {
        det2 = b*b-4*a*c;
        det  = sqrt(det2);
        rootp = (-b + det)/a/2;
        rootm = (-b - det)/a/2;
        return [rootm,rootp]


    roots a b c = 
            det2 = b*b-4*a*c;
            det  = sqrt(det2);
            rootp = (-b + det)/a/2;
            rootm = (-b - det)/a/2;

Explain this code:

applyTwice :: (a -> a) -> a -> a  
applyTwice f x = f (f x)  

The first line is the definition for applyTwice. It’s defined as something that takes two inputs and returns one output.

The first input it takes is wrapped in brackets as it signifies is another function. This other function takes in an input of some data type a, and returns a result of the same data type.

The second input will have the same data type

In the second line we have the actual function itself.

(a->a) .is represented by the arbitrary letter f – remember this is a function not a value. e.g. it could be the function you know as the plus symbol + So if it helps imagine a plus symbol coming through here.

The middle a is represented by the arbitrary letter x.

We see that the applyTwice function, will apply the input x , to the function f , that’s what’s happening in the (f x), and then the result of that is fed back into the function f a second time

Here’s the end result

applyTwice (+3) 10  

Source: Higher Order Functions – Learn You a Haskell for Great Good!

How would you re-write this the Haskell way?

def max(x,y):
    if x > y:
        return x
        return y


    max x y = 
        if x > y
            then x
            else y

What is happening in this function. Be as detailed as you can:

data Area = Circle Float Float Float
surface :: Area -> Float
surface (Circle _ _ r) = pi * r ^ 2
main = print (surface $ Circle 20 30 10 )
  • A custom data type called Area is defined. This has a property (or value constructor as they are called in Haskell) called Circle that takes in 3 floats. Note that value constructors have to have a capital letter to begin with (e.g. Circle)
  • Source: Learn You a Haskell for Great Good!

Explain the difference between a type, a type variable, type class, instance, and class?

Let’s begin this answer with a facepalm at all these different overlapping things that Haskell has.

Type: Basically a data type. Wish they’d just called it DataType. Examples of Type are Int, Integer, Float, Double , Char, Bool, as well as others such as () and tuples, List, Maybe, Either, Ordering (will explain all these elsewhere though see here if you can’t wait that long)

Type variable: This is just a letter that represents a data type. You will see it quite often in function definitions. So for example the definition for the head function (which brings back the first item in a list) is:

head :: [a] -> a  

Here the a represents ‘some type of data of type a whatever that is’. This definition shows that the output of this function will be the same data type.

Here is another example in my simpleton mind: If I had a function that converted an into into a char then the definition would look like this :

int2char :: [a] -> b 

So this is saying the output of int2char (which btw is something I just made up) would take something of one data type, but output something of a different data type

Type class:

When a type is a part of a type class, it means that it supports and implements the behaviour that the type class describes. A type can however belong to multiple type classes. For example int is part of nine of the classes shown below. These are just some of the more common classes.

So let’s look at a function definition. Here is the == function:

ghci> :t (==)  
(==) :: (Eq a) => a -> a -> Bool  

We see that the function called == has a class constraint next to the a. (Eq a) means I’ll take in any data type provided it belongs to the Eq class. (Or in my head ‘Eq guild members ONLY’)

A type can be made an instance of a typeclass if it supports that behavior. The Int type is an instance of the Eq typeclass because the Eq typeclass defines behavior for stuff that can be equated. And because integers can be equated, Int is a part of the Eq typeclass. The real usefulness comes with the functions that act as the interface for Eq, namely == and /=. If a type is a part of the Eq typeclass, we can use the == functions with values of that type. That’s why expressions like 4 == 4 and “foo” /= “bar” typecheck.


Data types can become a member of an instance in two ways. Using deriving at the end:

data TrafficLight = Red | Yellow | Green deriving (Eq)

or using Instance

data TrafficLight = Red | Yellow | Green

instance Eq TrafficLight where
    Red == Red = True  
    Green == Green = True  
    Yellow == Yellow = True  
    _ == _ = False 


How would you create a custom type?

  1. You can create a custom (data) type either by basing it on an existing one such as:

type Pos = (Int,Int)

2. or creating a new type such as:

data MyType = Yes | No | Maybe

Note that the name of your type and your data values have to begin capital letters

3. For custom data types you can go further and specify the data type

data MyType = Yes [Char] | No [Char] | Maybe [Char]

Btw, behind the scenes these Haskell is actually treating the Yes No and Maybe as functions that take in a value of type [Char]. So you could say your values have a [Char] parameter passed to them and they then return a MyType as a result.

4. The MyType portion can also have a parameter

data Maybe a = Nothing | Just a

When MyType is then used in code you would pass it an int parameter . However this really starts to get confusing so please refer to Hutton (pg 94-95) or online material.

What is record syntax?

This is a data type that contains a record (multiple fields). So for example, if we wanted a Person data type in which we can store the first name, last name and age, we could define it as:

data Person = Person String String Int Float String String deriving (Show)  

Note using the same name after the equal sign is just convention – we could have called it “Record” instead. Also note we inserted deriving (show) at the end if you want it to print to screen in GCHi

Or we can write it this way:

data Person = Person { firstName :: String  
                     , lastName :: String  
                     , age :: Int  
                     } deriving (Show)  

Explain what this code is doing:

module Plutus.Contract(
) where
import Plutus.Contract.Types (Contract (..))

The second line is exposing functions of this module for use by other modules. The function that it is exposing, “Contract” is itself imported by this module from Plutus.Contract.Types.

Explain how this syntax works:

    info :: TxInfo
    info = scriptContextTxInfo ctx

    oracleInput :: TxOut
    oracleInput =
        ins = [ o
              | i <- txInfoInputs info
              , let o = txInInfoResolved i
              , txOutAddress o == addr
        case ins of
            [o] -> o
            _   -> traceError "expected exactly one oracle input"
  • This code is checking that there is only 1 Oracle input (on line 15)
  • The first use of let here is a “let expression”. Basically saying let <ins = a list …> in <then use that list here= …>
  • the ins = bit is then using “list comprehension” and can be read as: “o is the list we are defining, and do this by pulling in every txInfoInputs from the info variable and putting it into i, then pull out the txInfoResolved from i and put it into 0, but only where the address of o matches the transaction address of the Oracle (defined before this code snippet)
  • So after constructing the list of valid Oracles, we expect there to be only one valid element which is what we check on line 15


What is a curried function?

Currying is the technique of converting a function that takes multiple arguments into a sequence of function that takes a single argument. In Haskell we could rewrite:

prelude> myFunction a b c = a+b+c


prelude> myFunction = (a -> (b -> (c ->a+b+c)))
prelude> myFunction 1 2 3

What are guards?

These allow the code to branch off into different actions depending on a condition

bmiTell :: (RealFloat a) => a -> String  
bmiTell bmi  
    | bmi <= 18.5 = "You're underweight, you emo, you!"  
    | bmi <= 25.0 = "You're supposedly normal."  
    | bmi <= 30.0 = "You're a bit overweight"  
    | otherwise   = "You're quite overweight"

How would you re-write this the Haskell way?

  Red = 1
    Blue = 2
    Yellow = 3

    color = set_color();
    action = case color 
        when Red then action1()
        when Blue then action2()
        when Yellow then action3()


    data Color = Red | Blue | Yellow

    color = set_color
    action = case color of
        Red -> action1
        Blue -> action2
        Yellow -> action3

What is a functor?

A functor simply apples a function to a every element that is provided to it

What is an applicative functor?

What is a monoid?

What is a monad?

%d bloggers like this: