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 [ ]: