## Scala 5 - Currying 본문

Tutorials/Scala

### Scala 5 - Currying

hashnut 2019. 4. 23. 19:00

스칼라 튜토리얼, scala에서 currying에 대해 알아보자

### Currying

Why we use currying? Let's look at the code, simplified by currying.

``````def sum(f: Int=>Int, a: Int, b: Int): Int =
if (a <= b) f(a) + sum(f, a+1, b) else 0
def linear(n: Int) = n
def square(n: Int) = n * n
def cube(n: Int) = n * n * n
def sumLinear(a: Int, b: Int) = sum(linear, a, b)
def sumSquare(a: Int, b: Int) = sum(square, a, b)
def sumCubes(a: Int, b: Int) = sum(cube, a, b)``````

We want the following. How?

``````def sumLinear = sum(linear)
def sumSquare = sum(square)
def sumCubes = sum(cube)``````
• Solution :
``````def sum(f: Int => Int): (Int, Int) => Int = {
def sumF(a: Int, b: Int): Int =
if (a <= b) f(a) + sumF(a+1, b) else 0
sumF
}

def sumLinear = sum(linear)
def sumSquare = sum(square)
def sumCube = sum(cube)

sumSquare(3, 10) + sumCubes(5, 20)

// We don't need to define the wrapper funcitons

sum(suquare)(3, 10) + sum(cube)(5, 20)``````

We can also write as follows :

``````def sum(f: Int => Int): (Int, Int) => Int =
(a, b) => if (a <= b) f(a) + sum(f)(a+1, b) else 0``````

If compiler is not smart as we expected, above function should make anonymous function `(a, b) => sth` everytime `sum(f)` is called, so it could be slower than the function using `sumF`. (Because anonymous function doesn't have its own name, however, once `sumF` is defined, it is used without need to be declared again) But if the compiler optimizes this code(because `sum(f)` is used recursively), it may not be executed slow.

Or more simply, and probably the best way :

``````def sum(f: Int => Int)(a: Int, b: Int): Int =
if (a <= b) f(a) + sum(f)(a+1, b) else 0``````

### Currying and Uncurrying

A function of Type

`(T1, T2, ... ,Tn) => T`

can be turned into one of type

`T1 => (T2 => (... Tn =>T)`

This is called 'currying' named after Haskell Brooks Curry. The opposite direction is called 'uncurrying'.

### Currying using Anonymous Functions

``````def foo(x: Int, y: Int, z: Int)(a: Int, b: Int) =
x + y + z + a + b

val f1 = (x: Int, z: Int, b: Int) => foo(x, 1, z)(2, b)

val f2 = foo(_: Int, 1, _: Int)(2, _: Int)

val f3 = (x: Int, z: Int) => (b: Int) => foo(x, 1, z)(2, b)

f1(1, 2, 3)
f2(1, 2, 3)
f3(1, 2)(3)``````

### Excercise : Cutty the mapReduce function

``````def mapReduce(combine: (Int,Int)=>Int,inival: Int,
f: Int=>Int, a: Int, b: Int): Int = {
if (a <= b) combine(f(a),mapReduce(combine,inival,f,a+1,b))
else inival
}

def sum(f: Int=>Int, a: Int, b: Int): Int =
mapReduce((x,y)=>x+y,0,f,a,b)

def product(f: Int=>Int, a: Int, b: Int): Int =
mapReduce((x,y)=>x*y,1,f,a,b)``````
• Solution :
``````def mapReduce2(combine: (Int, Int) => Int, inival: Int)
(f: Int => Int) (a : Int, b: Int): Int ={
if (a <= b) combine(f(a), mapReduce2(combine, inival)(f)(a+1, b))
else inival
}

// need to make a closure since mapReduce is param. code.
def sum2 = mapReduce2((x, y) => x+y, 0) _

// val is better than def. Think about why.
// If you use 'def', you need to 'close' your code everytime is is called.
val product2 = mapReduce2((x, y) => x*y, 1) _

sum2(linear)(0,100)
product2(square)(1, 5)``````

You need to add '_'(underbar) after `def sum2` so that it explicitly declares include methods invisible.

(Error message : missing argument list for method mapReduce2 in class. Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing `mapReduce2 _` or `mapReduce2(_,_)(_)(_,_)` instead of `mapReduce2`.)

### Exceptions

``````class factRangeException(val arg: Int) extends Exception

def fact(n: Int): Int =
if (n < 0) throw new factRangeException(n)
else if (n == 0) 1
else n * fact(n -1)

def foo(n : Int) = fact(n + 10)

try {
println(fact(3))
println(foo(-100))
} catch {
case e : factRangeException => {
println("fact range error: " + e.arg)
}
}``````

#### 'Tutorials > Scala' 카테고리의 다른 글

 Scala 7 - Datatypes2  (0) 2019.04.23 2019.04.23 2019.04.23 2019.04.23 2019.04.23 2019.04.23