Mike Slinn

Partially Applied Functions

— Draft —

Published 2013-10-21. Last modified 2015-09-20.
Time to read: 7 minutes.

Partially applied functions are used in many libraries. You can use them without realizing how they work. This lecture removes the mystery so you can write them. An anti-pattern for partial function usage is shown, followed by a working example of a pattern that uses partial functions to design layered software where each layer is independent of the layers below.

The sample code for this lecture is provided in the courseNotes/src/main/scala/PartiallyApplied.sc Scala worksheet.

Partially applied functions could also be called partially bound functions. These lecture notes will refer to partially applied Functions to emphasize that is what they are. More precisely, partially applied functions are instances of FunctionN, where N might vary between 1 and 22.

Terminology

Let’s agree upon some terminology before we go any further.

  • A parameter declares the name and type of a value that must be supplied by the caller to a method or FunctionN that receives the value.
  • An argument is the value of an expression passed to a method or FunctionN when it is called.

Here are examples of parameters and arguments:

Scala code
/** @param param1 is a String parameter that must be supplied
  * @param param2 is an Int parameter that must be supplied */
def myMethod(param1: String, param2: Int) = ???
// Invoke myMethod with two arguments: "argument1" and 2 myMethod("argument1", 2)

Recall from the Functions are First Class lecture of the Introduction to Scala course that you can lift a method from an object into a FunctionN instance by following the method body with an underscore. Lifting is also called eta expansion.

Scala REPL
scala> val tooString = "Tootoo!".toString _
tooString: () => String = <function0>
scala>
tooString() res1: String = Tootoo!
Lifting is also called eta expansion

Partially applied Functions are created when you supply some, but not all arguments to a FunctionN instance; you can fill in the remaining arguments when you invoke the function at a later time.

As an analogy, let’s pretend a tandem bicycle is like a FunctionN; each seat can be considered as an FunctionN parameter, and adding riders to the bicycle is similar to binding arguments to the parameter list. Seats are places for riders to sit, just like parameters are slots for providing arguments to methods.

Setting the Scene

We need two case classes to set the scene for this lecture’s example:

Scala code
case class Rider(name: String, weight: Double, height: Double)  {
  override def toString = s"""$name is $weight pounds and $height" tall"""
}
case class Bike(rider1: Rider, rider2: Rider, rider3: Rider, color: String) { lazy val riders = List(rider1, rider2, rider3) lazy val names = riders.map(_.name) lazy val totalWeight: Double = riders.map(_.weight).sum lazy val cheer = s"Go $color team" + riders.map(_.name).mkString(": ", ", ", "!") }

Now we’ll create some Rider instances:

Scala REPL
scala> val chloe = new Rider("Chloe",  124, 62)
chloe: Rider = Chloe is 124.0 pounds and 62.0" tall
scala>
val louise = new Rider("Louise", 136, 68) louise: Rider = Louise is 136.0 pounds and 68.0" tall
scala>
val beth = new Rider("Beth", 112, 59) beth: Rider = Beth is 112.0 pounds and 59.0" tall

Partially Applying the apply Method

Recall from the Case Classes lecture of the Introduction to Scala course that the automatically generated companion objects of case classes have apply factory methods. We are now ready to create a partially applied FunctionN from a companion object’s apply method. We can do that two ways:

  1. Eta Expansion. If we partially apply the apply method then we get a function that, given case class arguments as parameters, creates an instance of the class.
    Scala REPL
    scala> val newRider = Rider.apply _
    newRider: (String, Double, Double) => Rider = <function3>
    scala>
    newRider("Monique", 140, 69) res0: Rider = Monique is 140.0 pounds and 69.0" tall
  2. Placeholder syntax. Provide an underscore and the expected type for any method parameter for which you do not wish to bind a value:
    Scala REPL
    scala> val team1 = Bike(chloe, louise, beth, _: String)
    team1: String => Bike = <function1> 


    Remember that in Scala, underscores perform many purposes. A common purpose is to act as a placeholder, and that is what it is used for in the above. The underscore tells the compiler to lift the Bike.apply method to a new Function1 instance that just has the first 3 arguments applied (we say the three first three parameters of the Function1 instance are bound to the arguments chloe, louise and beth). The fourth parameter will be supplied later (it is unbound). Let’s do that now.

    Scala REPL
    scala> val bike1 = team1("red")
    bike1: Bike = Bike(Chloe is 124.0 pounds and 62.0" tall,Louise is 136.0 pounds and 68.0" tall,Beth is 112.0 pounds and 59.0" tall,red)
    scala>
    bike1.cheer res2: String = Go red team: Chloe, Louise, Beth!
    If a partially applied function has N unbound parameters then it is represented by a FunctionN
    If we had supplied four arguments to Bike.apply, a new Bike would have been returned. Instead, the type for team1 is String => Bike, or in other words it is a Function1 that accepts a String (for the Color parameter) and returns a Bike; this Function1 is actually a Bike factory. The underscore provided in place of the last parameter caused a partially applied Function1 to be returned instead of a Bike instance.

Underscores for placeholder syntax can be used in place of any argument, and they can appear in any position. Here is an example.

Scala REPL
scala> val oneRider = Bike(_: Rider, _: Rider, chloe, _: String)
oneRider: (Rider, Rider, String) => Bike = <function3> 

oneRider has type (Rider, Rider, String) => Bike, which means that it is a Function3 instance that accepts three parameters (Rider, Rider, String) and returns a Bike. Yes, it is also a factory method. We can bind two more arguments to oneRider, and create another partially applied function, like this.

Scala REPL
scala> val team2 = oneRider(louise, beth, _: String)
team2: String => Bike = <function1> 

You can see that team2 was created by calling oneRider and supplying two of the necessary arguments to create a Bike. Because an underscore was provided in place of the last argument, team2 is also a partially applied Function1 with type String => Bike. This means we can now produce a Bike by binding a String to team2 for the color argument.

Scala REPL
scala> val bike2 = team2("blue")
bike2: Bike = Bike(Louise is 136.0 pounds and 68.0" tall,Beth is 112.0 pounds and 59.0" tall,Chloe is 124.0 pounds and 62.0" tall,blue)
scala>
s"""bike2 riders are: ${bike2.names.mkString(", ")}""" res3: String = bike2 riders are: Louise, Beth, Chloe

Curried Functions

A method with multiple parameter lists is lifted into a chain of FunctionNs. As you know, Function1 instances accept only one parameter. Chains of Function1 instances are called curried functions.

You can curry a regular method or FunctionN by using Scala’s built-in curried method. Since it makes no sense to curry a Function1, let’s curry oneRider, which is a Function3.

Scala REPL
scala> oneRider.curried
res5: Rider => (Rider => (String => Bike)) = <function1> 

The returned type is interesting: Rider => (Rider => (String => Bike)). Let’s pull that apart. I’ve color-coded the chained Function1s so you can recognize them more easily. What we have is a Function1 that transforms a Rider into something which we can represent with X.

Scala code
Rider => X

...where X is also a Function1 that transforms a Rider into something which we can represent as Y.

Scala code
Rider => Y

Pulling Y apart, we discover yet another Function1 that transforms a String into a Bike.

Scala code
String => Bike

Now you know how to recognize a curried method signature – a chain of Function1s followed by a final type. The Function1 chain is written as nested parentheses.

We can use our knowledge of how method lifting works to curry the automatically generated Bike.apply method; recall that it accepts four parameters.

Scala REPL
scala> val curriedBike = (Bike.apply _).curried
curriedBike: Rider => (Rider => (Rider => (String => Bike))) = <function1> 

As you see, we get a similar return type as before: a chain of Function1s.

The companion object of a case class has a convenience method, also called curried, that also curries the companion object’s apply method.

Scala REPL
scala> val curriedBike2 = Bike.curried
curriedBike2: Rider => (Rider => (Rider => (String => Bike))) = <function1> 

Regardless of how you curried apply you can invoke it by providing all its parameters like this:

Scala REPL
scala> val bike3 = curriedBike(chloe)(louise)(beth)("yellow")
bike3: Bike = Bike(Chloe is 124.0 pounds and 62.0" tall,Louise is 136.0 pounds and 68.0" tall,Beth is 112.0 pounds and 59.0" tall,yellow) 

Creating Partially Applied Functions From Curried Functions

Notice that each of the arguments to curriedBike were provided as separate parameter lists. This is helpful because curried functions can be turned into partially applied Functions two ways: using the placeholder shorthand sytax that we saw a moment ago, and by using a second shorthand that is convenient for many circumstances.

Method 1: Placeholder Shorthand

Here are some examples of creating partially applied functions using placeholder shorthand:

Scala REPL
scala> val oneRiderB1 = Bike.curried(chloe)(_: Rider)(_: Rider)(_: String)
oneRiderB1: (Rider, Rider, String) => Bike = <function3>
scala>
val oneRiderB2 = curriedBike(_: Rider)(louise)(_: Rider)(_: String) oneRiderB2: (Rider, Rider, String) => Bike = <function3>
scala> scala>
val oneRiderB3 = Bike.curried(_: Rider)(_: Rider)(beth)(_: String) oneRiderB3: (Rider, Rider, String) => Bike = <function3>

Method 2: Eta Expansion Without the Trailing Underscore

The second type of shorthand is just like eta expansion, but without the trailing underscore. This type of shorthand is only possible for curried functions. For examples, if only the first parameter is bound to an argument, the remaining parameters can simply be ignored, and a partially applied FunctionN will be created.

Scala REPL
scala> val oneRiderC = Bike.curried(chloe)
oneRiderC: Rider => (Rider => (String => Bike)) = <function1> 

We can bind the second Rider parameter using the same syntax:

Scala REPL
scala> val twoRiders = oneRiderC(louise)
oneRiderC: (Rider => (String => Bike) = <function1>) 

We can bind the third Rider parameter using the same syntax.

Scala REPL
scala> val team4a = twoRiders(beth)
team4a: String => Bike = <function1> 

Finally, invoking team4a and supplying an argument for the color parameter yields a Bike.

Scala REPL
scala> val bike4 = team4a("green")
bike4: Bike = Bike(Chloe is 124.0 pounds and 62.0" tall,Louise is 136.0 pounds and 68.0" tall,Beth is 112.0 pounds and 59.0" tall,green)
scala>
val weight4 = s"Team ${bike4.color} weighs ${bike4.totalWeight} pounds" weight4: String = Team green weighs 372.0 pounds

Uncurrying

We can also uncurry a function, which creats an equivalent function with one parameter list.

Scala REPL
scala> val uncurriedBike = Function.uncurried(curriedBike)
uncurriedBike: (Rider, Rider, Rider, String) => Bike = <function4>
scala>
val bikex = uncurriedBike(chloe, louise, beth, "Brown") bikex: Bike = Bike(Chloe is 124.0 pounds and 62.0" tall,Louise is 136.0 pounds and 68.0" tall,Beth is 112.0 pounds and 59.0" tall,Brown)

Note that currying and uncurrying only works on FunctionNs, not methods.

Scala REPL
scala> Bike.apply.curried
<console>:9: error: missing arguments for method repeat2 in object R2;
follow this method with `_’ if you want to treat it as a partially applied function
              R2.repeat2.curried 

AntiPatterns: Eager Evaluation and Closing Over External State

We defined an unless construct in the More Fun With Functions lecture of the Introduction to Scala course.

Scala code
def unless(cond: Boolean)(body: => Unit): Unit = if (!cond) body

And we were able to call it like this:

Scala REPL
scala> var x = 1
x: Int = 1
scala>
unless(x == 0) { println(s"I can divide by x because x is not zero: ${3 / x}") } I can divide by x because x is not zero: 3
scala>
x = 0 x: Int = 0
scala>
unless(x == 0) { println(s"I can divide by x because x is not zero: ${3 / x}") }

The argument bound to the body parameter of the unless method above is an example of a closure, which was discussed in the Closures lecture of the Introduction to Scala course. That code works fine when used as shown. However, you should be cautious about making partially applied functions from arbitrary methods and functions without considering eager evaluation issues and possible race conditions when closing over mutable state. Here is an example of both types of errors in action.

Scala REPL
scala> x = 1
x: Int = 1
scala>
val isNonZero = unless(x==0) _ isNonZero: (=> Unit) => Unit = <function1>
scala>
isNonZero { println(s"x==$x") } x==1
scala>
var x = 0 x: Int = 0
scala>
isNonZero { println(s"x==$x") } // we expect no result to be printed, right? x==0

The problem that manifested is that the unless method eagerly evaluates the cond parameter, which means that the value of x when the partially applied function was created is used, and the value of the parameter is never recomputed. The problem goes away if we lazily evaluate that parameter.

Scala REPL
scala> def unless(cond: => Boolean)(body: => Unit): Unit = if (!cond) body
unless: (cond: => Boolean)(body: => Unit)Unit
scala>
val isNonZero = unless(x==0) _ isNonZero: (=> Unit) => Unit = <function1>
scala>
isNonZero { println(s"x==$x") } // no result is printed, which is correct %}

Even with this correction, the closure passed to the partially applied function and bound to the body parameter is sensitive to race conditions when the value of x changes, which we will discuss in the MultiThreading lecture. Partially applied functions should not reference mutable state in outer scopes. Instead, to be safe, partially applied functions should receive parameters for all external mutable state.

Pattern: Designing Layered Software

Let’s write a small program that simulates a graffiti artist. The SprayPaint class will be designed such that the base functionality is contained within the class, but higher-level functionality is defined elsewhere. We will use partially applied functions to drop in sequences of operations using SprayPaint. This allows us to define another higher-level class that uses the SprayPaint class to make paintings or write messages.

Let’s start the design of the SprayPaint class by just defining the properties.

Scala code
/** @param color pigment color of this spray can of paint
  * @param capacityRemaining milliliters of paint remaining in this can
  * @param gramsPerMeter amount of paint required to spray a line one meter long
  * @param maybeWhenLastShaken Some(Date) of when the can was last shaken, defaults to None which means never shaken */
case class SprayPaint(
  color: String,
  var capacityRemaining: Double,
  gramsPerMeter: Double,
  secondsBetweenShakes: Long=15,
  var maybeWhenLastShaken: Option[Date]=None
) {
  // more methods will go here
}

Now let’s add some low-level methods to this class.

Scala code
@inline def distanceRemaining: Double = capacityRemaining / gramsPerMeter
@inline def doesNotNeedShaking: Boolean = maybeWhenLastShaken.exists(new Date().getTime - _.getTime <= secondsBetweenShakes * 1000)
@inline def isEmpty: Boolean = capacityRemaining==0
@inline def needsShaking = !doesNotNeedShaking
@inline def nonEmpty: Boolean = !isEmpty
@inline def shake(): Unit = maybeWhenLastShaken = { println("Can is ready to spray") Some(new Date) }

The spray method uses many of the other methods.

Scala code
@inline def spray(distance: Double, degrees: Double) = {
  if (needsShaking) println("Cannot spray, need to shake the paint can")
  else {
    val remaining = distanceRemaining
    if (remaining<distance) println (f"Spraying $distance%.1f meters @ $degrees%.1f degrees.")
    else println (f"Spraying $remaining%.1f meters @ $degrees%.1f degrees.")
    capacityRemaining = math.max(0, capacityRemaining-gramsPerMeter*distance)
    if (isEmpty) println("Can is empty.")
  }
}

Here is one more method for the SprayPaint class, rather important for this code example. It accepts three parameter lists: the first is a java.awt.Point which acts as a reference point for the operation to be performed. The second parameter list is a scale factor; larger values will affect the amount of surface area for the operation. The third parameter list is a Function3[Point, Double, SprayPaint, Unit] that defines the operation to be carried out with the block of code. This is an examples of a pattern common to partial functions: if one of the parameters of a curried function is an operation, it needs to be a FunctionN that accepts at all of the other parameters plus an instance of the enclosing class, so it can do something with them.

Scala code
@inline def doIfNonEmpty(origin: Point)
                        (scale: Double)
                        (action: (Point, Double, SprayPaint) => Unit): Unit =
  if (nonEmpty) action(origin, scale, this) else println("Sorry, paint can is empty")

The PatternArtist class knows how to draw things with spray paint. Notice how drawTriangle is a partial function defined using placeholder syntax that accepts an origin (a Point) and a scale factor (a Double), and draws a pattern using the SprayPaint methods.

Scala code
case class PatternArtist(sprayPaint: SprayPaint) {
  val drawTriangle = sprayPaint.doIfNonEmpty(_: Point)(_: Double) { (origin, scale, self) =>
    self.shake()
    self.moveTo(new Point((origin.getX*scale).toInt, (origin.getY*scale).toInt))
    self.spray(scale, 45)
    self.spray(scale, -45)
    self.spray(scale, 180)
    if (self.nonEmpty)
      println(f"Spray paint can has ${self.capacityRemaining}%.1f milliliters remaining and can spray ${self.distanceRemaining}%.1f meters more.")
  }
}

This allows us to write high-level code that is independent of the SprayPaint implementation, and also independent of the PatternArtist implementation.

Scala code
// draw concentric green triangles
val greenArtist = PatternArtist(SprayPaint("green", 355, 6.3))
val origin = new Point(0, 0)
greenArtist.drawTriangle(origin, 1.0)
greenArtist.drawTriangle(origin, 2.0)
greenArtist.drawTriangle(origin, 3.0)

Now you know how to use partial functions to design layered software where each layer is independent of the layers below.

You can run this code example by typing:

Shell
$ sbt "runMain Banksy"
Can is ready to spray
Moving nozzle to 0.0, 0.0
Spraying 56.3 meters @ 45.0 degrees.
Spraying 55.3 meters @ -45.0 degrees.
Spraying 54.3 meters @ 180.0 degrees.
Spray paint can has 336.1 milliliters remaining and can spray 53.3 meters more.
Can is ready to spray
Moving nozzle to 0.0, 0.0
Spraying 53.3 meters @ 45.0 degrees.
Spraying 51.3 meters @ -45.0 degrees.
Spraying 49.3 meters @ 180.0 degrees.
Spray paint can has 298.3 milliliters remaining and can spray 47.3 meters more.
Can is ready to spray
Moving nozzle to 0.0, 0.0
Spraying 47.3 meters @ 45.0 degrees.
Spraying 44.3 meters @ -45.0 degrees.
Spraying 41.3 meters @ 180.0 degrees.
Spray paint can has 241.6 milliliters remaining and can spray 38.3 meters more. 

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.