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>.

  1. One way to think of partial application is that as each argument is provided, a parameter is dropped and the arguments value is "wired" into the expression for the function, producing a new function with one less argument.

    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 :: Int
    
    then 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!

  2. 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 50
    
    Try it, and see if you see any hint that one's a partial application and one was defined with let.
  3. 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
    
  4. 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!

  5. 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
    
  6. 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 give f a Char, it'll give you back a function that wants an Int. If you give that function an Int, it'll give you back a function that wants a Bool. If you give that function a Bool, it will give you a Bool.

    1. See if you can predict the type of f. Use :t Data.Char.chr to remind yourself of the type of the chr function.
    2. Try this series of operations:
      f
      f 'A' 65 True
      f 'B' 65 False
      let f1 = f 'A'
      let f2 = f1 65
      let r = f2 True
      r
      
    3. 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.

  7. 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
    
  8. 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
    
  9. Check the type of logBase and then create and use a partial application:

    :type logBase
    let log2 = logBase 2
    :type log2
    log2 65
    
  10. Write a function between first last value that returns True iff value 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.

  11. 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?

  12. 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?