- Concepts of Programming Languages

Methods and Functions: Currying

Instructor:

Learning Objectives

How are methods in object-oriented programming and functions in functional programming related?

  • Identify and describe the difference between methods and functions in Scala
  • Identify and describe the difference between tupled and curried definitions
  • Identify and use partial function application

Functional Programming

  • We say that functions are first-class if they can be
    • declared within any scope,
    • passed as arguments to other functions, and
    • returned as results of functions.
  • Functions foreach, map, filter are higher-order functions
    • they take a function as argument
    • Also common: return a function as the result

Paired Methods

  1. def add(x:Int, y:Int) = x+y
  2. add(11, 21)
  1. add: (x: Int, y: Int)Int
  2. res: Int = 32
  • This is the usual style of methods that take multiple arguments
  • It is a method that
    • Takes a pair of Ints
    • Returns an Int

Curried Methods

  1. def add(x:Int)(y:Int) = x+y
  2. add(11)(21)
  1. add: (x: Int)(y: Int)Int
  2. res: Int = 32
  • This is a curried definition
  • It is a method that
    • Takes an Int
    • Returns a method of type (y:Int)Int
    • So together the type of the method is add2: (x:Int)(y:Int)Int

Functions

  • Scala has first-class support for both functions and methods

Method

  1. def add(x:Int, y:Int) = x+y
  2. add(1,2)
  • Part of a class structure
  • Can be overridden
  • Has access to fields

Function

  1. val add = (x:Int, y:Int) => x+y
  2. add(1,2)
  • Can be passed as arguments, returned, assigned to variables

Functions

  1. val add = (x:Int, y:Int) => x+y
  2. add(11, 21)
  1. add: (Int, Int) => Int = $$Lambda$4576/0x00000008018d1840@6ae4d2ad
  2. res: Int = 32
  • This is a function that
    • Takes a pair of Ints
    • Returns an Int

Function Notation

  • Using lambda notation
    1. val add = (x:Int, y:Int) => x+y
    2. add(11,21)
  • Use underscore when parameters used exactly once
    1. val add = (_:Int) + (_:Int)
    2. add(11,21)
  • Types may be inferred in some contexts
    1. var add : (Int,Int)=>Int = _ + _
    2. add(11,21)

Curried Functions

  1. val add = (x:Int) => (y:Int) => x+y
  2. add(11)(21)
  1. add: Int => (Int => Int) = $$Lambda$...
  2. res: Int = 32
  • This is a curried definition
  • It is a function that
    • Takes an Int
    • Returns a function of type Int=>Int

Curried Methods

  1. def add(x:Int) = (y:Int) => x+y
  2. add(11)(21)
  1. add: (x: Int)Int => Int
  2. res: Int = 32
  • You can mix the notations
  • This is a method that
    • Takes an Int
    • Returns a function of type Int=>Int

Functions vs. Methods

  1. def add1(x:Int, y:Int) = x+y
  2. def add2(x:Int)(y:Int) = x+y
  3. val add1f = add1 _
  4. val add2f = add2 _
  1. add1: (x: Int, y: Int)Int
  2. add2: (x: Int)(y: Int)Int
  3. add1f: (Int, Int) => Int = $$Lambda$...
  4. add2f: Int => (Int => Int) = $$Lambda$...
  • Another use of wildcard operator _
    • don't care pattern
    • anonymous function expression

Partial Application

  1. val add4 = (x:Int) => (y:Int) => x+y
  2. def add5(x:Int) = (y:Int) => x+y
  3. val add4p = add4(11)
  4. val add5p = add5(11)
  5. val r4 = add4p(21)
  6. val r5 = add5p(21)
  1. add4: Int => (Int => Int) = $$Lambda$
  2. add5: (x: Int)Int => Int
  3. add4p: Int => Int = $$Lambda$
  4. add5p: Int => Int = $$Lambda$
  5. r4: Int = 32
  6. r5: Int = 32

Functions and Methods

  1. def a (x:Int) = x + 1
  2. val b = (x:Int) => x + 1
  3. val c = new Function[Int,Int] {
  4. def apply(x:Int) = x + 1
  5. }
  6. val d : PartialFunction[Matchable, Int] = {
  7. case i: Int => i + 1
  8. }
  9. for f <- List(a,b,c,d) yield f(4)
  1. fs: List[Int => Int] = List($$Lambda$, $$Lambda$, <function1>, <function1>)
  2. res1: List[Int] = List(5, 5, 5, 5)
  • What's going on here?

Functions and Methods

  • def defines a method with explicit parameter types
  • => defines a function with inferable parameter types
  • Functions are objects with method apply
    • Function e:X=>Y gets compiled to an object
      1. object e:
      2. def apply(x:X) : Y = ...
    • Function application e(args) is method invocation e.apply(args)

Summary

  • Tupled definitions: functions with multiple arguments
  • Curried definitions: a family of single-argument functions
  • In Scala, functions are objects with an apply method
  • Partial application creates new functions

Partial Application

  1. def add1(x:Int, y:Int) = x+y
  2. def add2(x:Int)(y:Int) = x+y
  3. val add3 = (x:Int, y:Int) => x+y
  4. val add4 = (x:Int) => (y:Int) => x+y
  5. def add5(x:Int) = (y:Int) => x+y
  6. val add1p = add1(11, _) /* x=>add1(11, x) */
  7. val add2p = add2(11)(_) /* x=>add2(11)(x) */
  8. val add3p = add3(11, _) /* x=>add3(11, x) */
  9. val add4p = add4(11)
  10. val add5p = add5(11)
  11. for f <- List(add1p, add2p, add3p, add4p, add5p)
  12. yield f(21)
  1. fs: List[Int => Int] = List($$Lambda$,$$Lambda$,$$Lambda$,$$Lambda$,$$Lambda$)
  2. res1: List[Int] = List(32, 32, 32, 32, 32)

# <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-robot fa-stack-1x fa-inverse"></i></span> Example - :fa fa-terminal: In Scala 3, implement a function to add two integer numbers. * :fa fa-comment-dots: ```scala def add(a: Int, b: Int): Int = { a + b } ``` * :fa fa-bolt-lightning: [Is this really a function or rather a method?](https://docs.scala-lang.org/tour/basics.html#methods) ---