In [4]:
    
final case class Box[A](value: A) // type이 A라고 정해짐, A가 잘 정의되면 어떤 것이든 담을 수 있음
    
    Out[4]:
In [5]:
    
Box(0)
// 알아서 타입 추론을 해서 맞춰줌
    
    Out[5]:
In [6]:
    
Box("foo")
    
    Out[6]:
In [7]:
    
def generic[A](in: A): A = in
    
    Out[7]:
In [8]:
    
generic[String]("foo")
    
    Out[8]:
In [9]:
    
generic("foo")
    
    Out[9]:
In [10]:
    
generic(1)
    
    Out[10]:
In [23]:
    
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]
    
    Out[23]:
In [24]:
    
val intList = Pair(1, Pair(2, Pair(3, End())))
    
    Out[24]:
In [25]:
    
val strList = Pair("a", Pair("b", Pair("c", End())))
    
    Out[25]:
In [ ]:
    
    
In [21]:
    
val sayHi = () => "Hi!"
// unit을 받아서 string을 토하는 함수
// lambda : 익명 함수
    
    Out[21]:
In [22]:
    
sayHi()
    
    Out[22]:
In [26]:
    
val add1 = (x: Int) => x + 1
    
    Out[26]:
In [27]:
    
add1(10)
    
    Out[27]:
In [27]:
    
add1("a")
    
    
    
In [28]:
    
val sum = (x: Int, y: Int) => x + y
    
    Out[28]:
In [29]:
    
sum(1, 2)
    
    Out[29]:
In [30]:
    
def oneAndTwo[A](f: (Int, Int) => A): A = f(1,2)
// 메서드에서 함수를 인자로 받음
    
    Out[30]:
In [31]:
    
oneAndTwo((x, y) => x + y)
// 여기선 type을 안적어도 됨 (위에서 Int로 정의함)
    
    Out[31]:
In [32]:
    
oneAndTwo((x, y) => s"($x, $y)")
    
    Out[32]:
In [33]:
    
(_: Int) + 1 // (x: Int) => x + 1
// x를 한번만 쓰이니까 굳이 쓸 필요가 뭐있나! 이런 느낌
    
    Out[33]:
In [34]:
    
res32(1)
    
    Out[34]:
In [34]:
    
// 파라미터 타입을 추론할 수 있는 경우엔 타입을 생략할 수 있습니다
_ + _ // (a, b) => a + b
foo(_) // (a) => foo(a)
foo(_, b) // (a) => foo(a, b)
_(foo) // (a) => a(foo)
    
In [35]:
    
case class Pair[A, B](a: A, b: B) {
    def calc[C](f: (A, B) => C): C = f(a, b)
}
    
    Out[35]:
In [36]:
    
Pair(1, 2).calc(_+_)
// 길게 쓰면 아래와 같이 씀!
    
    Out[36]:
In [39]:
    
val caseInt = Pair(1, 2).calc((a, b) => a+b)
    
    Out[39]:
In [37]:
    
Pair("foo", "bar").calc(_+_)
    
    Out[37]:
In [39]:
    
// 짧아지긴 하지만.. 아직 적응이 잘 안될거에요.. 계속 하다보면..!
    
In [40]:
    
object Adder {
    def add1(n: Int): Int = n+1
}
    
    Out[40]:
In [40]:
    
Adder.add1
// error 발생
    
    
    
In [41]:
    
Adder.add1 _
// 함수로 바꿔줌
    
    Out[41]:
In [42]:
    
Adder.add1(_)
// 함수로 바꿔줌
    
    Out[42]:
In [43]:
    
case class Box[A](a: A) {
    def modify[B](f: A => B): B = f(a)
}
    
    Out[43]:
In [44]:
    
Box(42).modify(Adder.add1)
    
    Out[44]:
In [45]:
    
Box(42) modify Adder.add1
    
    Out[45]:
In [45]:
    
Box("foo") modify Adder.add1
// add1은 Int를 받는 함수인데 foo는 string이라 error 발생
    
    
    
In [46]:
    
def curring(x: Int)(y: Int): Int = x + y
    
    Out[46]:
In [47]:
    
curring(1)(2)
    
    Out[47]:
In [48]:
    
def generic[A, B](a: A, f: A => B): B = f(a)
    
    Out[48]:
In [48]:
    
generic(1, _ + 1)
// 왜 컴파일이 안될까요?
// 파라미터 타입이 없음..!
    
    
    
In [48]:
    
generic(1, a=> a + 1)
// 타입 추론은 괄호 단위로만 가능. 
// a:A를 유추해 f:A를 쓸 수 없습니다. 명시적으로 적어줘야 함
    
    
    
In [50]:
    
def genericCurring[A, B](a: A)(f: A => B): B = f(a)
    
    Out[50]:
In [51]:
    
genericCurring(1)(_ + 1)
    
    Out[51]:
In [54]:
    
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")
    
    
    Out[54]:
In [52]:
    
case class Pair[A, B](one: A, two: B)
    
    Out[52]:
In [55]:
    
Tuple2("foo", 1)
    
    Out[55]:
In [56]:
    
("foo", 1)
    
    Out[56]:
In [57]:
    
("foo", 1, true)
    
    Out[57]:
In [58]:
    
("foo", 1) match { case (a, b) => a + b}
    
    Out[58]:
In [59]:
    
val x = (1, "b", true)
    
    Out[59]:
In [60]:
    
x._1
    
    Out[60]:
In [61]:
    
x._3
    
    Out[61]:
In [64]:
    
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]
    
    Out[64]:
In [62]:
    
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")
    
    
    
In [68]:
    
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]
    
    Out[68]:
In [ ]:
    
// 인자를 1개만 받음
// .value가 써있기 때문에 value를 넣어주면 됨
    
In [69]:
    
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")
    
    
    Out[69]:
In [71]:
    
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())
    
    
    
In [73]:
    
sealed trait Maybe[A]
final case class Empty[A]() extends Maybe[A]
final case class Just[A](value: A) extends Maybe[A]
    
    Out[73]:
In [74]:
    
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())
    
    Out[74]:
In [76]:
    
divide(10,1)
    
    Out[76]:
In [ ]:
    
    
In [ ]:
    
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]
    
In [76]:
    
val l = Pair(1, Pair(2, Pair(3, End())))
assert(l.map(_ + 1) == Pair(2, Pair(3, Pair(4, End()))))
println("ok")
    
    
    
In [77]:
    
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]
    
    Out[77]:
In [78]:
    
val l = Pair(1, Pair(2, Pair(3, End())))
assert(l.map(_ + 1) == Pair(2, Pair(3, Pair(4, End()))))
println("ok")
    
    
    Out[78]:
In [ ]:
    
def fold[B](init: B)(f: (A, B) => B): B = ???
    
In [ ]:
    
// 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)
    
In [79]:
    
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]
    
    Out[79]:
In [ ]:
    
// 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)))
    
In [80]:
    
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!")
    
    
    Out[80]:
In [81]:
    
sealed trait Maybe[A]
final case class Just[A](value: A) extends Maybe[A]
final case class Empty[A]() extends Maybe[A]
    
    Out[81]:
In [81]:
    
// 인자가 없으니 아래처럼 만들면 간단한데!
sealed trait Maybe[A]
final case class Just[A](value: A) extends Maybe[A]
final case object Empty extends Maybe[???]
    
    
    
In [82]:
    
sealed trait Maybe[A]
final case class Just[A](value: A) extends Maybe[A]
final case object Empty extends Maybe[Nothing]
// Nothing : 어떤 것들의 sub type!!!
    
    Out[82]:
In [82]:
    
// 명시적으로 해주는 것을 variance
    
In [82]:
    
val perhaps: Maybe[Int] = Empty
    
    
    
In [83]:
    
sealed trait Maybe[+A]
final case class Just[A](value: A) extends Maybe[A]
final case object Empty extends Maybe[Nothing]
    
    Out[83]:
In [ ]:
    
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
}
    
In [ ]:
    
case class Box[A](a: A) {
  def map[B](f: Function1[A, B]): Box[B] = Box(f(a))
}
    
In [83]:
    
case class Box[+A](a: A) {
  def set(a: A): Box[A] = Box(a) // 컴파일 안됨! 인자에만 들어갈 수 있음
}
    
    
    
In [ ]:
    
case class Box[+A](a: A) {
  def set[AA >: A](a: AA): Box[AA] = Box(a) 
}
// AA라는 super type이 있을 경우 AA를 넣어서 Box type을 만든다^^.. 이해 안가죠?
    
In [83]:
    
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]
    
    
    
In [84]:
    
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]
    
    Out[84]:
In [ ]:
    
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
    
In [ ]:
    
// 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))
    
In [ ]:
    
    
In [ ]:
    
    
In [ ]:
    
    
In [ ]: