Before working through these be sure you've got :m +Text.Show.Functions
in your ~/.ghci
(or ghc.conf
on Windows), or enter it as the first command in your ghci
session. If you don't, when a function value is displayed you'll get that error with No instance for (Show (a0 -> a0))...
instead of getting just <function>
.
Enter this function, which we'll use in several of the following exercises.
let f a b c d e = a + b + c + d + e :: Intthen try the following, paying close attention to the pattern with
f1
, f2
, ... fN
.
:set +t f let f1 = f 10 -- imagine f1 as being this: f1 b c d e = 10 + b + c + d + e :: Int -- note that: -- (1) 'a' has been removed from the parameter list -- (2) 'a' to the right of '=' has been replaced with 10 f1 let f2 = f1 20 -- imagine let f2 c d e = 10 + 20 + c + d + e :: Int f2 let f3 = f2 30 -- imagine let f3 d e = 10 + 20 + 30 + d + e :: Int f3 let f4 = f3 40 -- imagine let f4 e = 10 + 20 + 30 + 40 + e :: Int f4 let r = f4 50 -- imagine let r = 10 + 20 + 30 + 40 + 50 :: Int r
Each of f1
, f2
, f3
, f4
are partial applications—each was producing by calling a function with fewer arguments than it required.
If you don't understand the above example, BE SURE to let me know! I'm not kidding!
In the slides I forgot to point out that there's nothing special about a partial application: it's just a function like any other function. There's no way to tell it resulted from providing a function less arguments than it requires.
In the following we create two functions, fpartial
, a partial application of f
; and flet
, an equivalent computation defined with let.
let fpartial = f 10 let flet b c d e = 10 + b + c + d + e :t fpartial :t flet fpartial 20 30 40 50 flet 20 30 40 50Try it, and see if you see any hint that one's a partial application and one was defined with
let
.
In the previous example we bound partial applications to fN
but we could have used it
, too. Try these:
f 10 it 20 it 30 it 40 it 50
In the examples above we create a series of partial applications by supplying one argument at a time. Let's try supplying two arguments at a time.
We're still working with the function f
defined in the first exercise.
let f1 = f 10 20 -- imagine let f1 c d e = 10 + 20 + c + d + e f1 let f2 = f1 30 40 -- imagine let f2 e = 10 + 20 + 30 + 40 + e f2 let r = f2 50 -- imagine let r = 10 + 20 + 30 + 40 + 50 r
If you don't understand the above example, BE SURE to let me know! I'm not kidding!
Let's repeat the previous exercise using it
instead of binding names to intermediate results with let
.
f 10 20 it 30 40 it 50
Let's work with a different function:
let f x y z = (x == chr y) == z
Here's a way to think about f
:
If you givef
aChar
, it'll give you back a function that wants anInt
. If you give that function anInt
, it'll give you back a function that wants aBool
. If you give that function aBool
, it will give you aBool
.
f
. Use :t Data.Char.chr
to remind yourself of the type of the chr
function.
f f 'A' 65 True f 'B' 65 False let f1 = f 'A' let f2 = f1 65 let r = f2 True r
In the first example above I show f1
, f2
, etc. as being versions of the function f
with parameters dropped and values plugged in. Follow that example and show f1
, f2
, and r
in the same way.
I like using the Data.Char
functions for simple experiments; I've got :m +Data.Char
in my ~/.ghci
, so I don't need to bother with loading that module, or using a fully qualified name, like Data.Char.chr
.
After handouts were printed I added a diagram to slide 71 that shows a model of partial application using boxes with inputs and outputs. You can find that diagram in the copy of the Haskell slides on Piazza.
Using the notation of that model, draw a diagram that corresponds to this sequence of operations:
let f x y z = (x == chr y) == z let f1 = f 'A' let f2 = f1 65
Then, draw a diagram that corresponds to this sequence of operations:
let f x y z = (x == chr y) == z let f1 = f 'B' 80
Now for a curveball! First, predict and check the type of the function f
below. Then predict and check the results of the following operations
let f a b c d = 10 f 1 2 it 3 it 4 f f f f it f
Check the type of logBase
and then create and use a partial application:
:type logBase let log2 = logBase 2 :type log2 log2 65
Write a function between first last value
iff that returns
Truevalue
is between first
and last
, inclusive.
Example of usage:
> between 10 20 15 True > between 'X' 'Z' 'Y' True
Next, bind isLower
to a partial application of between
that will test to see if isLower
's argument is a lower-case letter. See if you can replace the ...
below with code to make the expressions that follow work.
> let isLower = ... > isLower 'm' True > isLower 'X' False
Here are between
and isLower
:
let between first last value = value >= first && value <= last let isLower = between 'a' 'z'
Your first thought with let isLower = ...
might have been, "Where's the argument of isLower
?" Instead of defining a function
let isLower letter = letter >= 'a' && letter <= 'z'we can just plug a couple of values into
between
. Compare isLower
to between
. isLower
looks like between
with 'a' plugged in for first
and 'z' plugged in for last
. This a key idea, at the heart of partial application. Let me know if you don't understand it.
Also, notice how the arguments for between
have been arranged to facilitate this partial application. If it were between value first last
we couldn't create isLower
by making a partial application of between
.
Another problem! (Yes, even the answers have problems!) Define isUpper
and isDigit
, too.
Write a function tempTo units temperature to convert a temperature to either Celsius or Fahrenheit. It works like this:
> tempTo 'c' 32 0.0 > tempTo 'f' 0 32.0 > toC 212 100.0 > toF 0 32.0
What are toC and toF doing? How did I create them?
If you know UNIX/Linux, try this one.
Many UNIX shells have a command aliasing mechanism. A common bash alias is this:
alias ll="ls -l"
How are aliases like partial applications? How do they differ?