In [1]:
trait Show[A] {
def show(a: A): String
}
object Show {
val intCanShow: Show[Int] = new Show[Int] {
def show(int: Int): String = s"int $int"
}
}
Out[1]:
In [2]:
import Show._
println(intCanShow.show(42))
Out[2]:
In [6]:
object Show {
def show[A](a: A)(implicit sh: Show[A]): String = sh.show(a)
implicit val intCanShow: Show[Int] = new Show[Int] {
def show(int: Int): String = s"int $int"
}
}
In [6]:
import Show._
println(show(42))
Out[6]:
In [5]:
Show.show(42)
Out[5]:
In [6]:
Show.show("42")
In [ ]:
implicit val example1 = ???
implicit var example2 = ???
implicit def example3 = ???
implicit object example4 {}
In [7]:
def implicitly[T](implicit e: T): T = e
Out[7]:
In [8]:
import Show._
Out[8]:
In [8]:
implicitly[Show[Int]].show(42)
In [8]:
def show[A: Show](a: A): String = implicitly[Show[A]].show(a)
In [ ]:
def apply[A](implicit sh: Show[A]): Show[A] = sh
In [ ]:
def show[A: Show](a: A): String = Show.apply[A].show(a)
In [ ]:
def show[A: Show](a: A): String = Show[A].show(a)
In [8]:
trait ExampleTypeClass[A] {
def doSomething(in: A): Foo
}
object ExampleTypeClass {
def apply[A](implicit instance: ExampleTypeClass[A]): ExampleTypeClass = instance
}
In [8]:
implicit class ShowOps[A: Show](a: A) {
def show: String = Show[A].show(a)
}
In [ ]:
import Show._
println(42.show)
In [ ]:
implicit class IntOps(n: Int) {
def shout: Unit = for (i<-0 until n) println("Shout!")
def ! : Int = (1 /: (1 ton))(_ * _)
}
In [ ]:
3.shout
3(IntOps).shout //랑 동일
println(3!) // factorial을 구현
// Shout!
// Shout!
// Shout!
In [ ]:
implicit class ShowOps[A](val a: A) extends AnyVal {
def show(implicit sh: Show[A]): String = sh.show(a)
}
In [ ]:
trait Show[A] {
def show(a: A): String
}
object Show {
def apply[A](implicit sh: Show[A]): Show[A] = sh
def show[A: Show](a: A): String = Show[A].show(a)
implicit class ShowOps[A](val a: A) extends AnyVal {
def show(implicit sh: Show[A]): String = sh.show(a)
}
implicit val intCanShow: Show[Int] = new Show[Int] {
def show(int: Int): String = s"int $int"
}
}
In [ ]:
implicit val stringCanShow: Show[String] = new Show[String] {
def show(str: String): String = s"string $str"
}
In [ ]:
def instance[A](f: A => String): Show[A] = new Show[A] {
def show(a: A): String = f(a)
}
implicit val intCanShow: Show[Int] = instance(int => s"int $int")
implicit val stringCanShow: Show[String] = instance(str => s"string $str")
In [ ]:
implicit val intCanShow: Show[Int] = int => s"int $int"
implicit val stringCanShow: Show[String] = str => s"string $str"
In [ ]:
trait Show[A] {
def show(a: A): String
}
object Show {
def apply[A](implicit sh: Show[A]): Show[A] = sh
def show[A: Show](a: A): String = Show[A].show(a)
implicit class ShowOps[A](val a: A) extends AnyVal {
def show(implicit sh: Show[A]): String = sh.show(a)
}
implicit val intCanShow: Show[Int] = int => s"int $int"
implicit val stringCanShow: Show[String] = str => s"string $str"
}
In [ ]:
implicit def tupleCanShow[A, B](implicit sa: Show[A], sb: Show[B]): Show[(A, B)] = {
case (a, b) => s"tuple (${a.show}, ${b.show})"
}
In [ ]:
implicit def tupleCanShow[A: Show, B: Show]: Show[(A, B)] = {
case (a, b) => s"tuple (${a.show}, ${b.show})"
}
In [ ]:
trait Show[A] {
def show(a: A): String
}
object Show {
def apply[A](implicit sh: Show[A]): Show[A] = sh
def show[A: Show](a: A): String = Show[A].show(a)
implicit class ShowOps[A](val a: A) extends AnyVal {
def show(implicit sh: Show[A]): String = sh.show(a)
}
implicit val intCanShow: Show[Int] = int => s"int $int"
implicit val stringCanShow: Show[String] = str => s"string $str"
implicit def tupleCanShow[A: Show, B: Show]: Show[(A, B)] = {
case (a, b) => s"tuple (${a.show}, ${b.show})"
}
}
In [ ]:
println((2, 2).show)
In [10]:
trait Eq[A] {
def eq(a1: A, a2: A): Boolean
}
object Eq {
implicit val eqInt: Eq[Int] = new Eq[Int] {
def eq(a1: Int, a2: Int): Boolean = a1 == a2
}
}
Out[10]:
In [11]:
assert(implicitly[Eq[Int]].eq(1, 1) == true)
assert(implicitly[Eq[Int]].eq(1, 2) == false)
println("OK!")
In [11]:
trait Eq[A] {
def eq(a1: A, a2: A): Boolean
}
object Eq {
def apply[A](implicit eq: Eq[A]): Eq[A] = eq
implicit val eqInt: Eq[Int] = _ == _
}
assert(Eq[Int].eq(1, 1) == true)
assert(Eq[Int].eq(1, 2) == false)
println("OK!")
In [11]:
//밖에서 정의하기
trait Eq[A] {
def eq(a1: A, a2: A): Boolean
}
object Eq {
def apply[A](implicit eq: Eq[A]): Eq[A] = eq
implicit val eqInt: Eq[Int] = _ == _
}
implicit class EqOps[A](a: A)(implicit eq: Eq[A]) {
def ==== (that: A): Boolean = Eq[A].eq(a, that)
}
assert((1 ==== 1) == true)
assert((1 ==== 2) == false)
println("OK!")
In [ ]:
// 또 정리하면
In [ ]:
trait Eq[A] {
def eq(a1: A, a2: A): Boolean
}
object Eq {
def apply[A](implicit eq: Eq[A]): Eq[A] = eq
implicit val eqInt: Eq[Int] = _ == _
}
implicit class EqOps[A](val a: A) extends AnyVal {
def ==== (that: A)(implicit eq: Eq[A]): Boolean = eq.eq(a, that)
}
assert((1 ==== 1) == true)
assert((1 ==== 2) == false)
println("Ok!")
In [11]:
trait Eq[A] {
def eq(a1: A, a2: A): Boolean
}
object Eq {
def apply[A](implicit eq: Eq[A]): Eq[A] = eq
implicit val eqInt: Eq[Int] = _ == _
implicit val eqStr: Eq[String] = _ == _
}
implicit class EqOps[A](val a: A) extends AnyVal {
def ==== (that: A)(implicit eq: Eq[A]): Boolean = eq.eq(a, that)
}
assert((1 ==== 1) == true)
assert((1 ==== 2) == false)
assert(("foo" ==== "foo") == true)
assert(("foo" ==== "bar") == false)
println("Ok!")
In [ ]:
trait Eq[A] {
def eq(a1: A, a2: A): Boolean
}
object Eq {
def apply[A](implicit eq: Eq[A]): Eq[A] = eq
implicit val eqInt: Eq[Int] = _ == _
implicit val eqStr: Eq[String] = _ == _
implicit def eqList[A: Eq]: Eq[List[A]] = {
case (Nil, Nil) => true
case (x :: xs, Nil) => false
case (Nil, y :: ys) => false
case (x :: xs, y :: ys) => Eq[A].eq(x, y) && Eq[List[A]].eq(xs, ys)
}
}
implicit class EqOps[A](val a: A) extends AnyVal {
def ==== (that: A)(implicit eq: Eq[A]): Boolean = eq.eq(a, that)
}
assert((1 ==== 1) == true)
assert((1 ==== 2) == false)
assert(("foo" ==== "foo") == true)
assert(("foo" ==== "bar") == false)
assert((List(1, 2) ==== List(1, 2)) == true)
assert((List(1, 2) ==== List(1, 3)) == false)
assert((List("foo", "bar") ==== List("foo", "bar")) == true)
assert((List("foo") ==== List("foo", "bar")) == false)
println("Ok!")
In [ ]:
// list.mkString -> ['a','b','c] => abc
// list.mkString("[",",, ","]") => [a, b, c]
trait Show[A] {
def show(a: A): String
}
object Show {
def apply[A](implicit sh: Show[A]): Show[A] = sh
def show[A: Show](a: A): String = Show[A].show(a)
implicit class ShowOps[A](val a: A) extends AnyVal {
def show(implicit sh: Show[A]): String = sh.show(a)
}
implicit val intCanShow: Show[Int] = int => s"int $int"
implicit val stringCanShow: Show[String] = str => s"string $str"
implicit def tupleCanShow[A: Show, B: Show]: Show[(A, B)] = {
case (a, b) => s"tuple (${a.show}, ${b.show})"
}
implicit def listCanShow[A: Show]: Show[List[A]] = _ map Show[A].show mkString("list [", ", ", "]")
}
import Show._
assert(List("foo", "bar").show == "list [string foo, string bar]")
println("Ok!")
In [9]:
trait Monoid[A] {
def zero: A
def combine(a1: A, a2: A): A
}
object Monoid {
implicit val intMonoid: Monoid[Int] = new Monoid[Int] {
def zero: Int = 0
def combine(a1: Int, a2: Int): Int = a1 + a2
}
}
assert(implicitly[Monoid[Int]].zero == 0)
println("Ok!")
Out[9]:
In [11]:
// 미래를 대비해 instance를 정의해서 리팩토링하면
In [ ]:
trait Monoid[A] {
def zero: A
def combine(a1: A, a2: A): A
}
object Monoid {
def instance[A](z: A)(f: (A, A) => A): Monoid[A] = new Monoid[A] {
def zero: A = z
def combine(a1: A, a2: A): A = f(a1, a2)
}
implicit val intMonoid: Monoid[Int] = instance(0)(_ + _)
}
assert(implicitly[Monoid[Int]].zero == 0)
assert(implicitly[Monoid[Int]].combine(10, 20) == 30)
println("Ok!")
In [11]:
// implicity 가 없어도 되도록 리팩토링
In [ ]:
trait Monoid[A] {
def zero: A
def combine(a1: A, a2: A): A
}
object Monoid {
def apply[A](implicit m: Monoid[A]): Monoid[A] = m
def instance[A](z: A)(f: (A, A) => A): Monoid[A] = new Monoid[A] {
def zero: A = z
def combine(a1: A, a2: A): A = f(a1, a2)
}
implicit val intMonoid: Monoid[Int] = instance(0)(_ + _)
}
assert(Monoid[Int].zero == 0)
assert(Monoid[Int].combine(10, 20) == 30)
println("Ok!")
In [ ]:
// |+| 연산자가 가능하도록 리팩토링
In [ ]:
trait Monoid[A] {
def zero: A
def combine(a1: A, a2: A): A
}
object Monoid {
def apply[A](implicit m: Monoid[A]): Monoid[A] = m
def instance[A](z: A)(f: (A, A) => A): Monoid[A] = new Monoid[A] {
def zero: A = z
def combine(a1: A, a2: A): A = f(a1, a2)
}
implicit val intMonoid: Monoid[Int] = instance(0)(_ + _)
}
implicit class MonoidOps[A](val a: A) extends AnyVal {
def |+| (that: A)(implicit m: Monoid[A]): A = m.combine(a, that)
}
assert((10 |+| 20) == 30)
assert(Monoid[Int].zero == 0)
assert(Monoid[Int].combine(10, 20) == 30)
println("Ok!")
In [ ]:
// zero를 정의해서 Monoid[Int].zero를 Monoid.zero[Int]로 축약
trait Monoid[A] {
def zero: A
def combine(a1: A, a2: A): A
}
object Monoid {
def apply[A](implicit m: Monoid[A]): Monoid[A] = m
def zero[A: Monoid]: A = Monoid[A].zero
def instance[A](z: A)(f: (A, A) => A): Monoid[A] = new Monoid[A] {
def zero: A = z
def combine(a1: A, a2: A): A = f(a1, a2)
}
implicit val intMonoid: Monoid[Int] = instance(0)(_ + _)
}
implicit class MonoidOps[A](val a: A) extends AnyVal {
def |+| (that: A)(implicit m: Monoid[A]): A = m.combine(a, that)
}
assert((10 |+| 20) == 30) // 덧셈
assert((10 |+| Monoid.zero[Int]) == 10) // 항등원
assert((Monoid.zero[Int] |+| 10) == 10) // 항등원
assert((10 |+| 20 |+| 30) == (10 |+| (20 |+| 30))) // 결합법칙
println("Ok!")
In [ ]:
// list에서도 가능하도록 리팩토링
In [ ]:
trait Monoid[A] {
def zero: A
def combine(a1: A, a2: A): A
}
object Monoid {
def apply[A](implicit m: Monoid[A]): Monoid[A] = m
def zero[A: Monoid]: A = Monoid[A].zero
def instance[A](z: A)(f: (A, A) => A): Monoid[A] = new Monoid[A] {
def zero: A = z
def combine(a1: A, a2: A): A = f(a1, a2)
}
implicit val intMonoid: Monoid[Int] = instance(0)(_ + _)
implicit def listMonoid[A]: Monoid[List[A]] = instance(List.empty[A])(_ ::: _)
}
implicit class MonoidOps[A](val a: A) extends AnyVal {
def |+| (that: A)(implicit m: Monoid[A]): A = m.combine(a, that)
}
assert((List(1, 2, 3) |+| List(4, 5, 6)) == List(1, 2, 3, 4, 5, 6))
assert((List(1, 2, 3) |+| Monoid.zero[List[Int]]) == List(1, 2, 3))
assert((10 |+| 20) == 30) // 덧셈
assert((10 |+| Monoid.zero[Int]) == 10) // 항등원
assert((Monoid.zero[Int] |+| 10) == 10) // 항등원
assert((10 |+| 20 |+| 30) == (10 |+| (20 |+| 30))) // 결합법칙
println("Ok!")
In [11]:
// tuple도 가능하도록
In [ ]:
trait Monoid[A] {
def zero: A
def combine(a1: A, a2: A): A
}
object Monoid {
def apply[A](implicit m: Monoid[A]): Monoid[A] = m
def zero[A: Monoid]: A = Monoid[A].zero
def instance[A](z: A)(f: (A, A) => A): Monoid[A] = new Monoid[A] {
def zero: A = z
def combine(a1: A, a2: A): A = f(a1, a2)
}
implicit val intMonoid: Monoid[Int] = instance(0)(_ + _)
implicit def listMonoid[A]: Monoid[List[A]] = instance(List.empty[A])(_ ::: _)
implicit def tuple2Monoid[A, B](implicit ma: Monoid[A], mb: Monoid[B]): Monoid[(A, B)] =
instance((ma.zero, mb.zero)){ case ((a1, b1), (a2, b2)) => (ma.combine(a1, a2), mb.combine(b1, b2)) }
}
implicit class MonoidOps[A](val a: A) extends AnyVal {
def |+| (that: A)(implicit m: Monoid[A]): A = m.combine(a, that)
}
assert((10 |+| 20) == 30) // 덧셈
assert((10 |+| Monoid.zero[Int]) == 10) // 항등원
assert((Monoid.zero[Int] |+| 10) == 10) // 항등원
assert((10 |+| 20 |+| 30) == (10 |+| (20 |+| 30))) // 결합법칙
assert((List(1, 2, 3) |+| List(4, 5, 6)) == List(1, 2, 3, 4, 5, 6))
assert((List(1, 2, 3) |+| Monoid.zero[List[Int]]) == List(1, 2, 3))
val lhs = (List(1, 2), (1, List("a")))
val rhs = (List(3, 4), (2, List("b")))
assert((lhs |+| rhs) == (List(1, 2, 3, 4), (3, List("a", "b"))))
println("Ok!")
In [ ]:
trait Monoid[A] {
def zero: A
def combine(a1: A, a2: A): A
}
object Monoid {
def apply[A](implicit m: Monoid[A]): Monoid[A] = m
def zero[A: Monoid]: A = Monoid[A].zero
def instance[A](z: A)(f: (A, A) => A): Monoid[A] = new Monoid[A] {
def zero: A = z
def combine(a1: A, a2: A): A = f(a1, a2)
}
implicit val intMonoid: Monoid[Int] = instance(0)(_ + _)
implicit def listMonoid[A]: Monoid[List[A]] = instance(List.empty[A])(_ ::: _)
implicit def tuple2Monoid[A: Monoid, B: Monoid]: Monoid[(A, B)] =
instance((zero[A], zero[B])){ case ((a1, b1), (a2, b2)) => (a1 |+| a2, b1 |+| b2) }
}
implicit class MonoidOps[A](val a: A) extends AnyVal {
def |+| (that: A)(implicit m: Monoid[A]): A = m.combine(a, that)
}
assert((10 |+| 20) == 30) // 덧셈
assert((10 |+| Monoid.zero[Int]) == 10) // 항등원
assert((Monoid.zero[Int] |+| 10) == 10) // 항등원
assert((10 |+| 20 |+| 30) == (10 |+| (20 |+| 30))) // 결합법칙
assert((List(1, 2, 3) |+| List(4, 5, 6)) == List(1, 2, 3, 4, 5, 6))
assert((List(1, 2, 3) |+| Monoid.zero[List[Int]]) == List(1, 2, 3))
val lhs = (List(1, 2), (1, List("a")))
val rhs = (List(3, 4), (2, List("b")))
assert((lhs |+| rhs) == (List(1, 2, 3, 4), (3, List("a", "b"))))
println("Ok!")