What's the use case of this kind of function?

scala> def mytest(s:String)(f:String=>Int) = f(s)
def mytest(s: String)(f: String => Int): Int

scala> val test2 = mytest("building") _
val test2: (String => Int) => Int = $Lambda$1106/0x00000008010c7350@71687d8f

what use case will test2 be used for?
is this a partial function?

Thanks

This article covers what you are looking at Partially-Applied Functions (and Currying) in Scala | alvinalexander.com. In this case test2 os a partially applied function.

Usually this is just for syntactic reasons (in my experience).

Consider the following

val in = List(6,5,4,3,2,1,2,3,4,5,6)
def isFactorOf(n: number)(maybeFactor: number): boolean = ???

val isFactorOfFirst = isFactorOf(in.head)
val isFactorOfLast = isFactorOf(in.last)

in
  .filter(isFactorOfFirst)
  .map(x => x*x)
  .filter(isFactorOfLast)

This could also be written like this

val in = List(6,5,4,3,2,1,2,3,4,5,6)
def isFactorOf(n: number)(maybeFactor: number): boolean = ???

val  first = in.head
val last = in.last

in
  .filter(isFactorOf(first))
  .map(x => x*x)
  .filter(isFactorOf(last))

Or

val in = List(6,5,4,3,2,1,2,3,4,5,6)
def isFactorOf(n: number, maybeFactor: number): boolean = ???

val  first = in.head
val last = in.last

in
  .filter(isFactorOf(first, _))
  .map(x => x*x)
  .filter(isFactorOf(last, _))

(there are probably a miriad of other ways to write this).

In scala 2 currying is also used to help the compiler with type inference IIRC.

Consider this trivial scastie, the uncurried variant doesn’t infer the types correctly at the callsite (this is fixed in scala 3). Scastie - An interactive playground for Scala.

…

It will be interesting to see what others make of currying and partially applied functions though

2 Likes

is " Partially-applied functions" the same as “partial functions”? thank you.

No a partial function is a function that is potentially non complete in terms of handling all of it’s inputs. Here is a good article explaining it
https://alvinalexander.com/scala/how-to-define-use-partial-functions-in-scala-syntax-examples/

for example

// this partial function doesn't work for odd inputs 
val partial: PartialFunction[Int, Int] = {
   case d if d % 2 == 0 => d 
}


// we can then use it like so, odd numbers will always return -1
partial.applyOrElse(1, d => -1)
2 Likes

Another way of describing a “partial function” is to contrast it with its “opposite”, a “total function”.

Total Function:
A “total function” asserts the entire input range results in an output existing within the defined domain.

def isEven(value: Int): Boolean =
  value % 2 == 0

There is literally no value you can pass to isEven that will result in an “undefined” behavior. Every valid Int will definitively and correctly return either true or false.

Partial Function:
A “partial function” asserts some parts of the input range will result in an output existing within the domain, and the excluded parts of the input range will result in an output of “undefined” (which by default likely results in a RuntimeException).

def pfSquareRootFloor(value: Int): Int = {
  assert(value > -1, s"value[$value] must be greater than -1")
  //implementation omitted for brevity, but included at the Scastie Link
}

for (v <- 0 to 36)
  println(s"$v: ${pfSquareRootFloor(v)}")
println("Finished!")

Scastie Link

The pfSquareRootFloor function will throw a runtime AssertionError if passed a negative Int.


Translating a Partial Function into a Total Function:
To avoid “undefined” (and the runtime AssertionError), a “partial function” can be “wrapped” to produce a “total function”. This is also referred to as “lifting”, as in the partial function is “lifted” to a “total function”.

There are two approaches.

  1. Literally wrap the partial function with something that makes it a total function.
import scala.util.Try

def pfSquareRootFloor(value: Int): Int = {
  assert(value > -1, s"value[$value] must be greater than -1")
  //implementation omitted for brevity, but included at the Scastie Link
}

for (v <- -1 to 36)
  println(s"$v: ${Try(pfSquareRootFloor(v))}")
println("Finished!")

Scastie Link

  1. If you have access to modify the implementation, then the second approach is to design the function to be total which can then is clearly communicated by the type signature.

Here’s a solution based on Option:

def fSquareRoot(value: Int): Option[Int] = {
  if (value > -1)
    Some(
      //implementation omitted for brevity, but included at the Scastie Link
    )
  else
    None
}

for (v <- -1 to 36)
  println(s"$v: ${fSquareRootFloor(v)}")
println("Finished!")

Scastie Link

And here’s a solution based on Either:

def fSquareRootFloor(value: Int): Either[String, Int] = {
  if (value > -1)
    Right(
      //implementation omitted for brevity, but included at the Scastie Link
    )
  else
    Left(s"value[$value] must be greater than -1")
}

for (v <- -1 to 36)
  println(s"$v: ${fSquareRootFloor(v)}")
println("Finished!")

Scastie Link

1 Like

Here’s my related StackOverflow answer for the problem space:

1 Like