final case class Box[A](value: A) // type이 A라고 정해짐, A가 잘 정의되면 어떤 것이든 담을 수 있음 Box(0) // 알아서 타입 추론을 해서 맞춰줌 Box("foo") def generic[A](in: A): A = in generic[String]("foo") generic("foo") generic(1) sealed trait LinkedList[A] final case class End[A]() extends LinkedList[A] final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A] val intList = Pair(1, Pair(2, Pair(3, End()))) val strList = Pair("a", Pair("b", Pair("c", End()))) val sayHi = () => "Hi!" // unit을 받아서 string을 토하는 함수 // lambda : 익명 함수 sayHi() val add1 = (x: Int) => x + 1 add1(10) add1("a") val sum = (x: Int, y: Int) => x + y sum(1, 2) def oneAndTwo[A](f: (Int, Int) => A): A = f(1,2) // 메서드에서 함수를 인자로 받음 oneAndTwo((x, y) => x + y) // 여기선 type을 안적어도 됨 (위에서 Int로 정의함) oneAndTwo((x, y) => s"($x, $y)") (_: Int) + 1 // (x: Int) => x + 1 // x를 한번만 쓰이니까 굳이 쓸 필요가 뭐있나! 이런 느낌 res32(1) // 파라미터 타입을 추론할 수 있는 경우엔 타입을 생략할 수 있습니다 _ + _ // (a, b) => a + b foo(_) // (a) => foo(a) foo(_, b) // (a) => foo(a, b) _(foo) // (a) => a(foo) case class Pair[A, B](a: A, b: B) { def calc[C](f: (A, B) => C): C = f(a, b) } Pair(1, 2).calc(_+_) // 길게 쓰면 아래와 같이 씀! val caseInt = Pair(1, 2).calc((a, b) => a+b) Pair("foo", "bar").calc(_+_) // 짧아지긴 하지만.. 아직 적응이 잘 안될거에요.. 계속 하다보면..! object Adder { def add1(n: Int): Int = n+1 } Adder.add1 // error 발생 Adder.add1 _ // 함수로 바꿔줌 Adder.add1(_) // 함수로 바꿔줌 case class Box[A](a: A) { def modify[B](f: A => B): B = f(a) } Box(42).modify(Adder.add1) Box(42) modify Adder.add1 Box("foo") modify Adder.add1 // add1은 Int를 받는 함수인데 foo는 string이라 error 발생 def curring(x: Int)(y: Int): Int = x + y curring(1)(2) def generic[A, B](a: A, f: A => B): B = f(a) generic(1, _ + 1) // 왜 컴파일이 안될까요? // 파라미터 타입이 없음..! generic(1, a=> a + 1) // 타입 추론은 괄호 단위로만 가능. // a:A를 유추해 f:A를 쓸 수 없습니다. 명시적으로 적어줘야 함 def genericCurring[A, B](a: A)(f: A => B): B = f(a) genericCurring(1)(_ + 1) val pair1 = Pair("foo", 1) val pair2 = Pair(2, "bar") assert(pair1.one == "foo") assert(pair1.two == 1) assert(pair2.one == 2) assert(pair2.two == "bar") println("ok") case class Pair[A, B](one: A, two: B) Tuple2("foo", 1) ("foo", 1) ("foo", 1, true) ("foo", 1) match { case (a, b) => a + b} val x = (1, "b", true) x._1 x._3 sealed trait Sum[A, B] final case class First[A, B]() extends Sum[A, B] final case class Second[A, B]() extends Sum[A, B] assert(First[Int, String](1).value == 1) assert(Second[Int, String]("foo").value == "foo") val sum: Sum[Int, String] = Second("foo") val matched: String = sum match { case First(x) => x.toString case Second(x) => x } assert(matched == "foo") println("ok") sealed trait Sum[A, B] final case class First[A, B](value: A) extends Sum[A, B] final case class Second[A, B](value: B) extends Sum[A, B] // 인자를 1개만 받음 // .value가 써있기 때문에 value를 넣어주면 됨 assert(First[Int, String](1).value == 1) assert(Second[Int, String]("foo").value == "foo") val sum: Sum[Int, String] = Second("foo") val matched: String = sum match { case First(x) => x.toString case Second(x) => x } assert(matched == "foo") println("ok") def divide(a: Int, b: Int): Maybe[Int] = b match { case 0 => Empty() case _ => Just(a/b) } assert(divide(10, 2) == Just(5)) assert(divide(10, 0) == Empty()) sealed trait Maybe[A] final case class Empty[A]() extends Maybe[A] final case class Just[A](value: A) extends Maybe[A] def divide(a: Int, b: Int): Maybe[Int] = b match { case 0 => Empty() case _ => Just(a/b) } assert(divide(10, 2) == Just(5)) assert(divide(10, 0) == Empty()) divide(10,1) sealed trait LinkedList[A]{ def map[B](f: A => B): LinkedList[B] = } final case class End[A]() extends LinkedList[A] final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A] val l = Pair(1, Pair(2, Pair(3, End()))) assert(l.map(_ + 1) == Pair(2, Pair(3, Pair(4, End())))) println("ok") sealed trait LinkedList[A]{ def map[B](f: A => B): LinkedList[B] = this match { case End() => End[B]() case Pair(head, tail) => Pair[B](f(head) , tail.map(f)) } } final case class End[A]() extends LinkedList[A] final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A] val l = Pair(1, Pair(2, Pair(3, End()))) assert(l.map(_ + 1) == Pair(2, Pair(3, Pair(4, End())))) println("ok") def fold[B](init: B)(f: (A, B) => B): B = ??? // test val list0 = Pair(1, Pair(2, Pair(3, Pair(4, End)))) assert(list0.fold(0)(_ + _) == 10) val list1 = Pair(1, Pair(2, Pair(3, Pair(4, End)))) assert(list1.fold(1)(_ * _) == 24) sealed trait LinkedList[A]{ def fold[B](init: B)(f: (A, B) => B): B = this match{ case End() => init case Pair(head, tail) => f(head, tail.fold(init)(f)) } } final case class End[A]() extends LinkedList[A] final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A] // test val list = Pair(1, Pair(2, Pair(3, Pair(4, End)))) val filtered = list.flatMap{ case n if x % 2 == 0 => End case n => Pair(n, End) } assert(filtered == Pair(1, Pair(3, End))) sealed trait LinkedList[A] { def map[B](f: A => B): LinkedList[B] = this match { case End() => End[B]() case Pair(head, tail) => Pair[B](f(head), tail.map(f)) } def fold[B](init: B)(f: (A, B) => B): B = this match { case End() => init case Pair(head, tail) => f(head, tail.fold(init)(f)) } def flatMap[B](f: A => LinkedList[B]): LinkedList[B] = this match { case End() => End() case Pair(head, tail) => val headList: LinkedList[B] = f(head) val tailList: LinkedList[B] = tail.flatMap(f) headList ++ tailList } def ++(that: LinkedList[A]): LinkedList[A] = this match { case End() => that case Pair(head, tail) => Pair(head, tail ++ that) } } final case class End[A]() extends LinkedList[A] final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A] val list = Pair(1, Pair(2, Pair(3, Pair(4, End())))) val filtered = list.flatMap[Int]{ case n if n % 2 == 0 => End() case n => Pair(n, End()) } assert(filtered == Pair(1, Pair(3, End()))) println("Ok!") sealed trait Maybe[A] final case class Just[A](value: A) extends Maybe[A] final case class Empty[A]() extends Maybe[A] // 인자가 없으니 아래처럼 만들면 간단한데! sealed trait Maybe[A] final case class Just[A](value: A) extends Maybe[A] final case object Empty extends Maybe[???] sealed trait Maybe[A] final case class Just[A](value: A) extends Maybe[A] final case object Empty extends Maybe[Nothing] // Nothing : 어떤 것들의 sub type!!! // 명시적으로 해주는 것을 variance val perhaps: Maybe[Int] = Empty sealed trait Maybe[+A] final case class Just[A](value: A) extends Maybe[A] final case object Empty extends Maybe[Nothing] trait Function0[+R] { def apply: R } trait Function1[-A, +B] { def apply(a: A): B } trait Function2[-A, -B, +C] { def apply(a: A, b: B): C } case class Box[A](a: A) { def map[B](f: Function1[A, B]): Box[B] = Box(f(a)) } case class Box[+A](a: A) { def set(a: A): Box[A] = Box(a) // 컴파일 안됨! 인자에만 들어갈 수 있음 } case class Box[+A](a: A) { def set[AA >: A](a: AA): Box[AA] = Box(a) } // AA라는 super type이 있을 경우 AA를 넣어서 Box type을 만든다^^.. 이해 안가죠? sealed trait Sum[+A, +B] { def flatMap[C](f: B => Sum[A, C]): Sum[A, C] = this match { case Failure(v) => Failure(v) case Success(v) => f(v) } } final case class Failure[A](value: A) extends Sum[A, Nothing] final case class Success[B](value: B) extends Sum[Nothing, B] sealed trait Sum[+A, +B] { def flatMap[AA >: A, C](f: B => Sum[AA, C]): Sum[AA, C] = this match { case Failure(v) => Failure(v) case Success(v) => f(v) } } final case class Failure[A](value: A) extends Sum[A, Nothing] final case class Success[B](value: B) extends Sum[Nothing, B] sealed trait Expression { def eval: Sum[String, Double] = ??? } final case class Addition(left: Expression, right: Expression) extends Expression final case class Subtraction(left: Expression, right: Expression) extends Expression final case class Division(left: Expression, right: Expression) extends Expression final case class SquareRoot(value: Expression) extends Expression final case class Number(value: Double) extends Expression // test assert(Addition(Number(1), Number(2)).eval == Success(3)) assert(SquareRoot(Number(-1)).eval == Failure("Square root of negative number")) assert(Division(Number(4), Number(0)).eval == Failure("Division by zero")) assert(Division(Addition(Subtraction(Number(8), Number(6)), Number(2)), Number(2)).eval == Success(2.0))