Posted in scala, partial function

Partial Functions, Scala



Comment

Case?

casematch 가 없어도 쓰일 수 있다. 예를 들어

val m1 = Map(1 -> "one", 2 -> "two")  
m1 foreach { case(k, v) => println(s"k -> v" }  

사실, case 가 들어간 문장은 function 이다. Scala Doc 을 보면, scala.collection.immutable.Mapforeach 는 다음처럼 정의되어 있다.

def foreach(f: ((A, B)) => Unit): Unit  

Map vs Collect

여기 재미난 예제가 하나 더 있다. mapcollect 가 서로 다른 결과를 보여준다.

scala > val l = List(490, "Nexus5")  
scala > l map { case i: Int => i * 1000 }

scala.MatchError: Nexus5 (of class java.lang.String)  
  at $anonfun$1.apply(<console>:9)
  at $anonfun$1.apply(<console>:9)
  at scala.collection.immutable.List.map(List.scala:276)

문자열인 "Nexus5" 를 만나면 MatchError 가 발생한다. 반면 collect

scala > val l = List(490, "Nexus5")  
scala > l collect { case i: Int => i * 1000 }  
res11: List[Int] = List(49000)  

왜 그럴까? scala.collection.immutable.Listcollect 정의를 한번 보자.

def collect[B](pf: PartialFunction[A, B]): Map[B]  

function 이 아니고 PartialFunction 을 취한다.

Partial Function

Partial Function 을 이해하는 쉬운 방법은, 일반 FunctionTotal Function 으로 이해하면 된다. 즉, 일반 Function 이 주어진 인자에 대해 모든 값을 취한다면, Partial Function 은 주어진 타입에 대해서 특정 값만 취할 수 있다. Scala School 의 원문을 첨부하면

A function works for every argument of the defined type. In other words, a function defined as (Int) => String takes any Int and returns a String.
A Partial Function is only defined for certain values of the defined type. A Partial Function (Int) => String might not accept every Int.

val onlyOne : PartialFunction[Int, String] = { case 1 => "one" }

onlyOne.isDefinedAt(1) // true  
onlyOne.isDefinedAt(2) // false  

onlyOneInt 를 인자로 받지만, 그 중에서도 1 만 취해 "one" 을 돌려주는 PartialFunction[Int, String] 이다.

PartialFunction 또한 compose 될 수 있다.

val onlyOne : PartialFunction[Int, String] = { case 1 => "one" }  
val onlyTwo : PartialFunction[Int, String] = { case 2 => "two" }  
val wildcard : PartialFunction[Int, String] = { case _ => "else" }

val partial = onlyOne orElse onlyTwo orElse wildcard  

Case!

처음엔 caseFunction 을 만든다고 했지만, 사실 PartialFunction 을 정의한다. mapfilterFunction 을 받지만, PartialFunctionFunction 의 subtype 이므로 파라미터로 넘겨줄 수 있다.

case class Car(model: String, price: Int)

val list = List(Car("H", 3000), Car("A", 2800))

list filter { case Car(model, price) => price < 2900 }

// List[Car] = List(Car("A", 2800))

References

(1) http://blog.bruchez.name/2011/10/scala-partial-functions-without-phd.html
(2) https://twitter.github.io/scala_school/pattern-matching-and-functional-composition.html

Author

1ambda

Functional, Scala, Akka, Rx and Haskell