Haskell

From Organic Design

Haskell is a functional programming langauge used in Cardano.

Getting Started

Install the Haskell platform with:

sudo apt-get install haskell-platform

Then run the interpreter via:

ghci

Quitting the Haskell interpreter is the same as vim: :q or :quit.


You can write Haskell in .hs files then compile using ghc like so:

ghc -o hello hello.hs

And then you can run via the executable file ./hello.

Types

Haskell allows you to define types but this is optional if no type is declared it will infer the type. Types in Haskell must start with a capital letter.

5 -- Infers the type
5 :: Double -- Define the type.

-- Ask ghci what type '5' is.
:t 5

The unit type () is a type that has only one value () which is similar to void in other languages.

You can combine types easily in one of two ways: Tuples and Lists. Lists hold many values of the same type whereas Tuples can hold values of different types.

[1, 2, 3] -- Simple list with the values 1 to 3.
[1 .. 5] -- List with the values 1 to 5
[1, 3 .. 10] -- List of odd values between 1 and 10.
['H', 'e', 'l', 'l', 'o'] -- A String!

(1, true) -- Simple Tuple.
(1, True, 2.0, 2) -- A longer Tuple

zip [1 .. 5] ['a' .. 'e'] -- Combine the two lists into a list of tuples: [(1, 'a'), (2, 'b') ...]
map (+ 2) [1 .. 5] -- Map function on a list: [3,4,5,6,7]
filter (> 2) [1 .. 5] -- Filter function on a list: [3,4,5]
fst (1, 2) -- Get the first element of the Tuple.
snd (1, 2) -- Get the second element of the Tuple.
map fst [(1, 2), (3, 4), (5, 6)] -- Apply the first function to each tuple in the list: [1,3,5].

Functions

inc x = x+1 -- Define an increment function.
add x y = x + y -- Define an addition function that takes two parameters.
(\x -> \y -> x+y) 1 2 -- Lambda version of add taking 1 & 2 as it's parameters.

You can also give types to your functions:

inc :: Int -> Int -- inc is a function that takes an Int and outputs an Int.
inc x = x+1

add :: Int -> Int -> Int -- inc is a function that takes two Ints as parameters and outputs an Int.
add x y = x + y

-- Define the factorial function in parts using pattern matching.
fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact ( n - 1 )

-- Define the factorial function using guards, guards are like piecewise functions in mathematics. 
fact :: Integer -> Integer 
fact n | n == 0 = 1 
       | n /= 0 = n * fact (n-1)

-- You can split a complicated function into parts using 'where'
roots :: (Float, Float, Float) -> (Float, Float)  
roots (a,b,c) = (x1, x2) where 
   x1 = e + sqrt d / (2 * a) 
   x2 = e - sqrt d / (2 * a) 
   d = b * b - 4 * a * c  
   e = - b / (2 * a)

-- Haskell allows you to do function composition with the '.' operator.
composedFunc = outer.inner -- outer and inner are functions.
-- composedFunc will compute the inner function then pass the return value to the outer function, compute that and return.
-- Note: the return value from inner needs to match that of the parameter value of outer.


In Haskell you can have conceptually infinite functions!

numsFrom n = n : numsFrom (n+1) -- A list of all numbers (n -> Infinity) from the argument.
squares = map (^2) (numsFrom 0) -- A list of all squares of the positive integers.
fib = 1 : 1 : [ a+b | (a,b) <- zip fib (tail fib) ] -- The list of all Fibonacci numbers!

Infinite functions are cool, but you will probably want to get a finite sample from them:

take 10 fib -- The first 10 Fibonacci numbers.
filter (< 100) (take 30 fib) -- All Fib numbers less than 100.
filter odd (filter (<300) (take 30 fib)) -- All odd Fib numbers less than 300.

Parallel Programming

You can write multi-threaded or parallel programs using the parallel library, you can install a library like so:

cabal install --lib parallel
import Control.Parallel

main = a `par` b `par` c `pseq` print (a + b + c)
    where
        a = ack 3 10
        b = fac 42
        c = fib 34

fac 0 = 1
fac n = n * fac (n-1)

ack 0 n = n+1
ack m 0 = ack (m-1) 1
ack m n = ack (m-1) (ack m (n-1))

fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
./parallel -N3 # Use 3 cores.

Modules

You can split Haskell code with modules. Each module must be in a Haskell file with the same name:

Custom.hs

module Custom (
showEven,
showBoolean
) where

showEven:: Int-> Bool
showEven x = do
    if x `rem` 2 == 0
        then True
    else False

showBoolean :: Bool->Int
showBoolean c = do
    if c == True
        then 1
    else 0

main.hs

import Custom

main = do
    print(showEven 4)
    print(showBoolean True)

Custom Types

Data

-- Create the custom data type Shape that extends Show (so that print will output something)
data Shape =	Circle Float Float Float
	|	Rectangle Float Float Float Float
	deriving (Show)

-- A function for calculating the surface area.
surface :: Shape -> Float
surface (Circle _ _ r) = pi * r ^ 2
surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)

-- Override properties such as the == operator
instance Eq Shape where
	Circle _ _ r1 == Circle _ _ r2 = r1 == r2
	Rectangle x11 y11 x12 y12 == Rectangle x21 y21 x22 y22 = (abs $ x12 - x21) == (abs $ x22 - x21) && (abs $ y12 - y11) == (abs $ y22 - y21)
	_ == _ = False

-- Another custom data type with a different format.
data Animal = Animal {
	name :: String,
	legCount :: Int,
	height :: Float
} deriving (Show)

main = do
	print (surface $ Circle 10 20 10)
	print (Circle 10 20 10)

	let cow = Animal "Cow" 4 1.6

	-- Here we can output a specific value of our type.
	print (height cow)

Type

Types is a way to alias a name with another type.

-- Define PhoneNumber as the type String
type PhoneNumber = String
type Name = String

-- Define PhoneBook as the type of a list of tuples containing types Name and PhoneNumber
type PhoneBook = [(Name,PhoneNumber)]

-- Create a variable with type PhoneBook
phoneBook :: PhoneBook
phoneBook =
	[
		("name 1", "number 1"),
		("name 2", "number 2")
	]

Packages

Cabal is a system for building and packaging Haskell libraries and programs.


Search for a package:

cabal list <PACKAGE NAME>

Install a package with Cabal:

cabal install --lib <PACKAGE NAME>

See Also