Functional Programming Patterns
There are numerous patterns that anyone who wants to become a functional programmer needs to know. Without these patterns he will experience different forms of "stuckness". We use the underlying structure of the problems that we are trying to solve to uncover patterns that we can use to solve them.
The [GOF design patterns][https://en.wikipedia.org/wiki/Design_Patterns] are a good example of discovering structure in problems and codifying them into patterns.
Here we will review simple patterns that arise when solving problems using functional techniques. Becoming familiar with the patterns and understanding how they are used is a good start. Then depending on your aptitude/interest you can explore/learn the underlying mathematical structure.
The best way to explore some of these patterns are by looking at some interesting Programming problems. We can solve these problems using functional programming techniques.
Solving a version of the Expression Problem
The expression problem is very simple to state but solving this problem exposes some of the intricate properties of programming Languages.
Here is a concrete representation of the problem. Build a program that can evaluate mathematical expressions like
((3+5)*2)
.
Now notice that to solve this problem we need to support different operator types like +, -
etc. And support the addition of new operators say *, /
.
Additionally we have different ways to process an expression. For example we can either evaluate the expression or display the expression. We need to be able to support the addition of new ways to process expressions and also support the addition of new operators.
The solution shown below uses the following features of Elm:
- Algebraic Data types
- Expressions represented as recursive data structures
- Pattern matching is used to get type based dispatch
- Multiple processing functions (eval/render) for different behavior
- Recursion for Expression Tree traversal
import Graphics.Element exposing (..)
type Expr =
Val Float
|Add Expr Expr
|Sub Expr Expr
|Mul Expr Expr
|Div Expr Expr
eval : Expr -> Float
eval aExpr =
case aExpr of
Val x -> x
Add l r -> eval l + eval r
Sub l r -> eval l - eval r
Mul l r -> eval l * eval r
Div l r -> eval l / eval r
display : Expr -> String
display aExpr =
case aExpr of
Val x -> toString x
Add l r -> "(" ++ display l ++ "+" ++ display r ++ ")"
Sub l r -> "(" ++ display l ++ "-" ++ display r ++ ")"
Mul l r -> "(" ++ display l ++ "*" ++ display r ++ ")"
Div l r -> "(" ++ display l ++ "/" ++ display r ++ ")"
--building an expression for ((3+5)*2)
aExpr : Expr
aExpr = (Mul (Add (Val 3) (Val 5)) (Val 2))
main =
flow down [
show ("display aExpr : " ++ (display aExpr ))
,print "eval aExpr : " (eval aExpr )
]
--a helper function to make display easier
print message value = show (message ++ (toString value))
Function Chains
Here are a couple of problems that we can solve using function chains. This is a very powerful and general procedure that allows us to build complex functionality out of smaller/simpler functions.
Find the sum of the square of the numbers in a list greater than a threshold value.
import Graphics.Element exposing (..)
import String exposing (toList)
-- for each element of the list take the second power
-- then filter out the elements that are greater than the threshold
-- the sum all the elements
processList threshold =
List.map ((^)2)
>> List.filter (\x -> x > threshold)
>> List.foldr (+) 0
main =
flow down [
print "process list : " (processList 1 [1..100])
]
--a helper function to make display easier
print message value = show (message ++ (toString value))
Finding letter frequency in a string
Given a string find the letter frequency for letters in a given string.
import Graphics.Element exposing (..)
import List.Extra exposing (group)
import String exposing (toList)
--find the frequency of letters occurring in a string
findLetterFrequency : String -> List ( Maybe Char, Int )
findLetterFrequency str =
List.Extra.group (String.toList str)
|>List.map (\e -> ((List.head e), (List.length e)))
findLetterFrequency1 : String -> List ( Char, Int )
findLetterFrequency1 str =
List.Extra.group (String.toList str)
|>List.map (\e -> (extractChar (List.head e), (List.length e)))
extractChar : Maybe Char -> Char
extractChar m =
case m of
Just x -> x
Nothing -> ' '
main =
flow down [
print "process list : " (processList 1 [1..10])
,print "process list : " (findLetterFrequency "hello")
]
--a helper function to make display easier
print message value = show (message ++ (toString value))
Newton's Method for finding roots
Let us see if we can write a program to find the nthRoots
of a number.
This program illustrates the power of combining iteration/recursion with function calls, to create powerful programs.
This is called Newton's method, named after Isaac Newton. Here is the code implementing Newton's method:
import Graphics.Element exposing (show, flow, down)
import List exposing (..)
until : (a -> Bool) -> (a -> a) -> a -> a
until p f x = case (p x) of
True -> x
False -> until p f (f x)
-- Newtons Method
deriv f x =
let
dx = 0.0001
in (f(x + dx) - f(x))/dx
newton f = let
satis y = abs(f y) < eps
eps = 0.0001
improve y = y-(f y/deriv f y)
in until satis improve
sqRtNewton x = let f y = y^2 - x
in newton f x
cubeRtNewton x = let f y = y^3 - x
in newton f x
main = flow down [
print "square root of 2 using Newton's method : " (sqRtNewton 3)
,print "cube root of 2 using Newton's method : " (cubeRtNewton 3)
]
--a helper function to make display easier
print message value = show (message ++ (toString value))
Coins Change problem - Greedy Algorithm
Using Tuples
import Graphics.Element exposing (show, flow, down)
import List exposing (..)
coins = [100, 50, 25, 10, 1]
initialAmount = 46
initialData : (Int, List Int)
initialData = (initialAmount, [])
makeChange : (Int, List Int) -> List Int -> (Int, List Int)
makeChange data coins =
foldl makeChangeForCoin data coins
makeChangeForCoin : Int -> (Int, List Int) -> (Int, List Int)
makeChangeForCoin coin data=
let remaining = fst data%coin
numCoins = fst data//coin
in (remaining, numCoins:: snd data)
main = show ("Given the coins " ++ toString(coins) ++ "to make change for the amount: "
++ toString(initialAmount) ++ " we need " ++
(toString (makeChange initialData coins |> snd |> reverse)))
Using Records
import Graphics.Element exposing (show, flow, down)
import List exposing (..)
coins =
[100, 50, 25, 10, 1]
initialAmount =
62
initialData :Data
initialData =
{amount = initialAmount, coinsUsed = []}
type alias Data =
{amount : Int, coinsUsed : List Int}
makeChange : Data -> List Int -> Data
makeChange data coins =
foldl makeChangeForCoin data coins
makeChangeForCoin : Int -> Data -> Data
makeChangeForCoin coin data =
let
remaining =
data.amount % coin
numCoins =
data.amount // coin
in {data | amount =
remaining
,coinsUsed =
numCoins::data.coinsUsed}
computeAndDisplayChange : String
computeAndDisplayChange =
makeChange initialData coins|> .coinsUsed |> reverse |> toString
main = show ("Given the coins " ++ toString(coins) ++ "to make change for the amount: "
++ toString(initialAmount) ++ " we need " ++
computeAndDisplayChange)