Introduction to Functional Programming

Sébastien BESNIER

initialModel =
    { count = 0 }


type Msg
    = Increment
    | Decrement


update msg model =
    case msg of
        Increment ->
            { model | count = model.count + 1 }

        Decrement ->
            { model | count = model.count - 1 }


view model =
    div [style "font-size" "200%"]
        [ button [ onClick Increment ] [ text "+1" ]
        , div [ style "color" "red" ] [ text <| String.fromInt model.count ]
        , button [ onClick Decrement ] [ text "-1" ]
        ]

https://ellie-app.com/fDyHsFVJCska1

Challenge: Reset to 0

No mutation! Only values and pure functions

 

 

No mutation! Only values and pure functions

 

Do you know impure functions?

See Elm architecture:

https://youtu.be/kEitFAY7Gc8?t=1476

eight = 5 + 3

Affectation

Try it online

eight = round 8.3

Function call

add a b = a + b

Function definition

add a b = a + b

-- call that function:
eight = add 7 1

Function definition

Challenge

Compute length of \(\texttt{"hello world"}\) and add 5 to it using the \(\texttt{add}\) function (in 1 expresion!)

String.length "abc" -- returns 3
eight = add 7 1

add a b = a + b

Order of declarations does not matter

add : Int -> Int -> Int
add a b = a + b

Types


String.length : ???

What is the type of String.length ?


String.length : String -> Int

What is the type of String.length ?


add3 a b c = a + b + c

What is the type of add3?

add3:
    Int
    -> Int
    -> Int
    -> Int
add3 a b c = a + b + c

Good practice:
add type anotation!

String.append "hello " "world" 
-- returns "hello world"

What is the type of String.append ?

String.append "hello " "world" 
-- returns "hello world"






String.append : String -> String -> String

What is the type of String.append ?

t = add 5 String.append "hey" "hoy"


f a b = 
   String.length (String.append a b)

g a b =
    add (String.append a a) b  
    
h a b =
    add (String.append a b) b

What is the type of the following expressions?


user = 
    { name = "Seb"
    , score = 42
    , city = "Paris"
    }

Records

user : User
user = 
    { name = "Seb"
    , score = 42
    , city = "Paris"
    }

Records

type alias User =
    { name : String
    , score : Int
    , city : String
    }

Records


newUser =
    { user | score = 43 }


newUserBis =
    { user | score = user.score + 1 }

Record updates

newUser : User
newUser =
    { user | score = 43 }

newUserBis : User
newUserBis =
    { user | score = user.score + 1 }

Record updates

rewardUser : User -> User
-- increase the score by one

Record updates

rewardUser : User -> User
rewardUser user =
    { user | score = user.score + 1 }

Record updates

Challenge: add type definition for the model in the counter example

Custom types

Records to store multiple data
(name, score, city....)

Custom types

Records to store multiple data
(name, score, city....)

What about data following various patterns ?

Modeling a shape

      Point                          Circle Float           Rectangle Float Float

Radius

Length

Width

What is the type of a shape?

Reminder: counter

type Msg
    = Increment
    | Decrement

First variant

 Second variant

Modeling a shape

      Point                          Circle Float           Rectangle Float Float

Radius

Length

Width

Modeling a shape

      Point                          Circle Float           Rectangle Float Float

Modeling a shape

type Shape =
    Point | Circle Float | Rectangle Float Float

Use a custom type

myRect = Rectangle 10 20

bigCircle = Circle 420000

describe : ???
describe shape =
  case shape of
    Point ->
      "A lonely point"
    Circle radius ->
      "A circle of radius " 
         ++ String.fromFloat radius
    Rectangle width height ->
      "A rectangle of area "
         ++ String.fromFloat (width*height)

Use a custom type

myRect = Rectangle 10 20

bigCircle = Circle 420000

describe : Shape -> String
describe shape =
  case shape of
    Point ->
      "A lonely point"
    Circle radius ->
      "A circle of radius " 
         ++ String.fromFloat radius
    Rectangle width height ->
      "A rectangle of area "
         ++ String.fromFloat (width*height)

Boolean

Defined by the language

-- Defined by Elm
type Bool = True | False

-- our code:
myBool = True

res = 
 case myBool of
    True -> 42
    False -> 18
    

Boolean

Defined by the language

-- Defined by Elm
type Bool = True | False

-- our code:
myBool = True

res =
 case myBool of
    True -> 42
    False -> 18
    
res =
  if myBool then
    42
  else
    18

Preferred way:

if(condition) {
   myVar = 42;
} else {
   myVar = 18;
}
myVar = condition ? 42 : 18;

VS

Conditionals

in imperative language (Java, C, Python...)

 

Conditionals

in imperative language (Java, C, Python...)

 

myVar = condition ? 42 : 18;

VS

INSTRUCTION:

changes the state

EXPRESSION:

compute a new value

if(condition) {
   myVar = 42;
} else {
   myVar = 18;
}

Implement highlight on hover

 

https://ellie-app.com/fF4BSNrDVK4a1

 

1st step: define Model & Msg and implement update

A bit of maths

  • Custom types are "sum" types
  • Records are "product" types
  • (Functions are "exponent" types)

That's all you need!

[] -- empty list
[1, 2, 6] : List number
["Hello", "world"] : List String

Lists

[] -- empty list
[1, 2, 6] : List number
["Hello", "world"] : List String
["Hello", 42] -- impossible!

Lists are homogeneous




ages : List Int
ages = [ 18, 14, 35, 10, 51 ]

agesInFiveYears : List Int
agesInFiveYears =
    -- add five to each element

No for-loops!

addFive : Int -> Int
addFive x = 5 + x

ages : List Int
ages = [ 18, 14, 35, 10, 51 ]

agesInFiveYears : List Int
agesInFiveYears =
    List.map addFive ages

No for-loops!

List.map :
    (a -> b)
    -> List a
    -> List b



agesInFiveYears =
    List.map addFive ages

Map signature

ages : List Int
ages = [ 18, 14, 35, 10, 51 ]

-- transform into : 
-- [ "18 ans", "14 ans", ... ]

-- Hint:
-- String.fromInt : Int -> Str

Map challenge

ages : List Int
ages = [ 18, 14, 35, 10, 51 ]

displayAge : Int -> String
displayAge age =
    String.fromInt age ++ " ans"

agesDisplayed =
    List.map displayAge ages

Map challenge

{-| Compute the total number of chars in the 
list of strings given in argument. Example:
    
    totalLength ["hey!", "I am", "Hungry!!"]
    
returns 16
-}
totalLength : List String -> Int
totalLength strings = ...


-- HINT: List.sum : List Int -> Int

Map challenge 2

{-| Compute the total number of chars in the 
list of strings given in argument. Example:
    
    totalLength ["hey!", "I am", "Hungry!!"]
    
returns 16
-}
totalLength : List String -> Int
totalLength strings =
  List.sum (List.map String.length strings)

Map challenge 2

Implement a Nim game

 

https://ellie-app.com/fF4PS3bjTb7a1

 

Rules:

  • 10 lucifers     at the beginning
  • 2 players
  • at his turn, the player can take 1,2 or 3 matches
  • the player taking the last lucifer lost

Talks

By groups of ~2, about 5-10 mins.

  1. Trees (the data structure): examples, how to implement it in elm, some basic algorithms (e.g. computing depth) -> Lionel, Teddy, Ilyane
  2. Modules: how do they work in elm? How do we use them to enforce some properties (give concrete examples)? Keyword: Opaque Type
    -> Hugo, Jacky
  3. List.foldr/List.foldl: explain how it works, give some examples (maybe compare some "for loop" implementation with foldX one?). How to write "map" from it?
    -> Léo, Luis
  4. Lists: How could we define the List type ourself? How to implement length/map/filter with this implementation? (to simplify, we could just define "List of Ints")
    -> Rémy, Nacer
  5. Maybe/Result: how do we handle errors in Elm? Give the definition of those types. Give examples with "map" (and if you feel it, "andThen")
    -> Maxime, Gaspard
-- True if the 1st arg is in the second arg
member : a -> List a -> Bool

-- True if any element of the list is True
any : List Bool -> Bool

-- True if all elements of the list is True
all : List Bool -> Bool

-- rewrite member using map and any

isAscending : List Int -> Bool

Exercises on list

implement those functions using recursion and List.foldl (except for the last: use only recursion)

import Dict exposing (Dict)

d1 = Dict.empty

d2 = Dict.insert "Seb" 42 d1

age = case Dict.get "Seb" d2 of
  Just a -> "Seb is " ++ String.fromInt a
  Nothing -> "I don't know Seb"

d3 = 
  Dict.fromList [("Seb", 42), ("George", 45)]

Dictionary

associate a key to a value

Dictionary

associate a key to a value

 

 

See the docs:

https://package.elm-lang.org/packages/elm/core/latest/Dict

 

Keys have to be comparable:

String, Int, Float, List and Tuples of comparable are comparable

Challenge:

People list!

https://github.com/sebsheep/intro-to-fp/tree/main/contact-list

 

Follow the TODOs!

Parsing

Parsing

type alias Affectation =
    { varName : String
    , value : Int
    }
    
fromString :
    String
    -> Affectation


fromString "foo = 3" 
--{varName="foo",value=3}

Challenges:

Introduction to Functional Programming

By sebbes

Introduction to Functional Programming

  • 437