CSE 230: Winter 2013

Polymorphism and Computation Patterns

Ranjit Jhala, UC San Diego

module HigherOrder where
import Prelude hiding ((++))

Plan

Polymorphic Functions

Polymorphic functions

Many functions are oblivious to the type of their arguments

doTwice f x = f (f x)

Works on many kinds of values

ghci> :type doTwice
doTwice :: (a -> a) -> a -> a

For ALL Types a

Polymorphic functions

Many functions are oblivious to the type of their arguments

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

Can call with Int values

ghci> doTwice (+1) 0
2


Type parameter a is instantiated as Int

Polymorphic functions

Many functions are oblivious to the type of their arguments

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

Or with on String values

ghci> doTwice (++ "I don't care! ") "Isn't this neat ?"

"Isn't this neat ? I don't care! I don't care! "


Type parameter a is instantiated as String

doTwice is Polymorphic

Works on many kinds of values

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

Crucial for abstraction

Polymorphism Enables Reuse

Works on many kinds of values

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

Reuse same doTwice across different operators and data

Polymorphism: With Great Power ...

... comes great responsibility!

Recall infix sections

ghci> (10 <) 12
True

Quiz: What is the value of?

ghci> doTwice (10 <) 100

Polymorphism: With Great Power ...

... comes great responsibility!

ghci> doTwice (10 <) 100

Nasty Type Error

doTwice :: (a -> a) -- operator 
        -> a        -- input
        -> a        -- output

Operator should return same type as output ...

... but (10 <) :: Int -> Bool

Quiz

Are you following so far ?


ex1 = doTwice doTwice

  1. What is the type of ex1 ?

  2. What is the type parameter of doTwice instantiated as?

Polymorphic Data

Polymorphic Data Types

Of course, you can also create polymorphic data types

data List a 
  = Nil             -- empty list
  | Cons a (List a) -- "cons" of a hd :: a and tl :: List a

also known as (defined in standard library)

data [a] 
  = []        -- empty list
  | (:) a [a] -- "cons" of a hd :: a and tl :: List a

Functions Over Polymorphic Data

Polymorphic functions and data go hand in hand ...

len :: [a] -> Int
len []     = 0
len (_:xs) = 1 + len xs

or our friends from last time

(++) :: [a] -> [a] -> [a] 
head :: [a] -> a
tail :: [a] -> [a]

Note:

  1. All the above oblivious to type of values inside list
  2. Types are pretty awesome specifications of behavior

FP's tag-team = Polymorphism + Higher-Order Funs

Computation Pattern 1: Iteration


Convert Strings to UPPERCASE

toUpperString []     = []
toUpperString (c:cs) = (??? c) : toUpperString cs

Er, what to put in ??? Let's ask Hoogle!

Computation Pattern 1: Iteration


Recall

type XY      = (Double, Double)
type Polygon = [XY]

A function that shifts a Polygon

shiftPoly d []       = []
shiftPoly d (xy:xys) = shiftXY d xy : shiftPoly d xys

Helper That Translates One Vertex

shiftXY (dx, dy) (x, y) = (x + dx, y + dy)

A recurring theme: iteration!


Like humans and monkeys toUpperString and shiftPoly are 93% same!

shiftPoly d []       = []
shiftPoly d (xy:xys) = shiftXY d xy : shiftPoly d xys

toUpperString []     = []
toUpperString (c:cs) = (toUpper c) : toUpperString cs

A recurring theme: iteration!


Like humans and monkeys toUpperString and shiftPoly are 93% same!

shiftPoly d []       = []
shiftPoly d (xy:xys) = shiftXY d xy : shiftPoly d xys


toUpperString []     = []
toUpperString (c:cs) = (toUpper c) : toUpperString cs

Capture the Pattern in a Bottle!

map          :: (a -> b) -> [a] -> [b]
map f []     = []
map f (x:xs) = (f x) : (map f xs)

Capture the Iteration Pattern in a Bottle!

map          :: (a -> b) -> [a] -> [b]
map f []     = []
map f (x:xs) = (f x) : (map f xs)

Instantiate the pattern

toUpperString = map toUpper
shiftPoly     = map shiftXY

Quiz: Dude, where are the parameters?

Patterns everywhere!

Computation Pattern 2: Folding


Function that Adds elements of a list

listAdd []     = 0
listAdd (x:xs) = x + (listAdd xs)

Computation Pattern 2: Folding


Function that Multiplies elements of a list

listMul []     = 1
listMul (x:xs) = x * (listMul xs)

Can you spot the pattern?


Pattern = Shared DNA

listAdd []     = 0
listAdd (x:xs) = x + (listAdd xs)

listMul []     = 1
listMul (x:xs) = x * (listMul xs)

Quiz: What are differences between listAdd and listMul?

Pattern = Shared DNA = foldr


listAdd []     = 0
listAdd (x:xs) = x + (listAdd xs)

listMul []     = 1
listMul (x:xs) = x * (listMul xs)

foldr op base []     = base
foldr op base (x:xs) = x `op` (foldr op base xs) 

listAdd = foldr (+) 0   -- op = (+), base = 0
listMul = foldr (*) 1   -- op = (*), base = 1

Pattern = Shared DNA = foldr


foldr op base []     = base
foldr op base (x:xs) = x `op` (foldr op base xs) 

Recall

len []     = 0
len (x:xs) = 1 + len xs

Quiz: Rewrite len with foldr!

len = foldr ex2 ex3 

What should ex2 and ex3 be?

Public Service Announcement

Public Service Announcement


What is more readable: HOF or Recursion ?

Spotting Patterns In the "Real World"

Spotting Patterns In the "Real World"

Patterns appear not just in toy functions but in real code

To practice, lets develop a small library that swizzles text files

  1. Beginner's version, riddled with explicit recursion

  2. Intermediate version, recursion eliminated with higher-order functions

  3. Advanced version, swizzle and unswizzle without duplication