Scala 의 Type Class 코드로 살펴보기 2

language | 29 June 2017

Tags | scala

1. Turning Type Classes into OO Classes




abstract class Iter[I,A]{
    def getValue(i : I) : Option[A]
    def getNext(i : I) : I
}
defined class Iter
implicit def ListIter[A] : Iter[List[A], A] = new Iter[List[A], A]{
    def getValue(i : List[A]) : Option[A] = i.headOption
    def getNext( i : List[A]) : List[A] = i.tail
}
defined function ListIter
def sumElements[I](xs : I)(implicit proxy : Iter[I, Int]) : Int = proxy.getValue(xs) match{
    case None => 0
    case Some(n) => n + sumElement(proxy.getNext(xs))
}

def printElements[I, X](xs : I)(implicit proxy : Iter[I, X]) : Unit = proxy.getValue(xs) match{
    case None =>
    case Some(n) => {println(n) ; printElements(proxy.getNext(xs)) ; }
}
defined function sumElements
defined function printElements
val I = List(3, 4, 5, 1)
sumElements(I)
printElements(I)
3
4
5
1





I: List[Int] = List(3, 4, 5, 1)
res5_1: Int = 13
import scala.language.higherKinds
import scala.language.implicitConversions

abstract class Dyn2[S[_,_], A]{
    type Data
    val d: Data
    val i : S[Data, A]
}

object Dyn2 {
    // 실제 동작 함수. ii 에 incIter 동작 인스턴스가 들어가고
    implicit def apply[S[_,_],D,A](dd : D)(implicit ii : S[D,A]) : Dyn2[S,A] = new Dyn2[S,A]{
        type Data = D
        val d : Data = dd
        val i : S[Data, A] = ii
    }
    implicit def methods[S[_,_], A](d : Dyn2[S,A]) : S[d.Data, A] = d.i
}


/*********************************************************************
<<  Dyn2 인스턴스 는 <타입클래스 정보 S > <반환할 데이터 타입 A> 로 초기화하고  
    <타입 클래스가 다룰 데이터 구조 D>를 입력받는다.    >>>   

    val  x : Dyn2[S,A](D)  >>>  x.d = 데이터구조 (주인공)  
                                    x.i = 타입클래스 인스턴스 정보( 실행기 )

    <Dyn2 인스턴스 x> 를 만들면 무엇을 할 수 있는가?
    x : Dyn2[Iter, Int]  =  Dyn2(List(1,2,3))   
    y : Dyn2[Iter, Int]  = Dyn2(MyNode(1, Empty(), Empty()))   
    각각의 x, y 에 안에 들어있는 데이터구조와 타입클래스 인스턴스 정보로, Iter 타입클래스로 구현된 메서드를 모두 사용 가능하다.

    Iter type class 의  interface method 들

    sumElements(x.d)(x.i)
    printElements(y.d)(y.i)




***********************************************************************/
import scala.language.higherKinds

import scala.language.implicitConversions


defined class Dyn2
defined object Dyn2
// type class 인스턴스  .... Int 데이터구조 들어왔을때  동작을 기술함 ... 하지만 직접 넣어줄 것이기 때문에 implicit 적지 않았다.
def incIter(max : Int) : Iter[Int, Int] = new Iter[Int, Int] {
    def getValue(i : Int) = if (i <= max) Some(i) else None
    def getNext(i : Int) = i + 1
}

// Dyn2 인스턴스를 생성해내는 방법
// getMyIter
def getMyIter(isInc: Boolean) : Dyn2[Iter, Int] = { //Dyn2[Iter, Int] 로 초기화한다.
    if (isInc) Dyn2(0)(incIter(10)) //Dyn2.apply(0)(incIter(10))   // Dyn2(0) Int라는 데이터구조 를 다룬다.
    else List(3, 1, 5)  // Dyn2.apply(List(3,1,5))(listIter[Int])   // Dyn2(List[Int])   List[Int] 라는 데이터구조를 다룬다.
}
defined function incIter
defined function getMyIter
val i1  : Dyn2[Iter, Int] = getMyIter(true)
printElements(i1.d)(i1.i)  //printElements(0)(incIter(10))

val i2 : Dyn2[Iter, Int] = getMyIter(false)
printElements(i2.d)(i2.i) //printElements()
0
1
2
3
4
5
6
7
8
9
10
3
1
5





i1: Dyn2[Iter, Int] = $sess.cmd6Wrapper$Helper$Dyn2$$anon$1@74086131
i2: Dyn2[Iter, Int] = $sess.cmd6Wrapper$Helper$Dyn2$$anon$1@67d4c1f6
abstract class Dyn[T[_]]{
    type Data
    val d : Data
    val proxy : T[Data]
}

object Dyn{
    implicit def apply[D, T[_]](dd : D)(implicit tt : T[D]) : Dyn[T] = new Dyn[T] {

        type Data = D
        val d : Data = dd
        val proxy : T[D] = tt

    }
}


defined class Dyn
defined object Dyn
// 데이터 구조 정보

sealed abstract class MyTree[A]
case class Empty[A]() extends MyTree[A]
case class Node[A](value : A, left : MyTree[A], right : MyTree[A]) extends MyTree[A]
defined class MyTree
defined class Empty
defined class Node

type class 본체

// 데이터 구조 R 를 다루는 type class 본체  
// iter 메서드는 Dyn2
abstract class Iterable[R,A]{
    def iter(a: R) : Dyn2[Iter , A] // 데이터구조 R 을 넣으면 R 을 다루는 Dyn2[Iter, A](R) 를 돌려줄 것이다.
}
defined class Iterable

type class 인스턴스

implicit def treeIterable : Iterable[MyTree[Int], Int] = new Iterable[MyTree[Int], Int]{
    def iter(a : MyTree[Int]) : Dyn2[Iter, Int] = {
        def go(I : MyTree[Int]) : List[Int] = I match{
            case Empty() => Nil
            case Node(v, left, right) => v :: (go(left) ++ go(right))
        }
        go(a)   // List[Int]   --> Dyn2[Iter, Int](List[Int]) --> Dyn2[Iter,Int](List[Int])(listIter(List[Int], Int))
    }
}
defined function treeIterable

type class 인터페이스 (사용)

def sumElements2[R](xs : R)(implicit proxy : Iterable[R, Int]) = {
    val cs = proxy.iter(xs)  // Dyn2[Iter, Int](Iter 로 다룰수 있게 변형된 xs)
    sumElements(cs.d)(cs.i)
}
defined function sumElements2
val t : MyTree[Int] = Node(1, Empty(), Empty())
sumElements2(t)
t: MyTree[Int] = Node(1,Empty(),Empty())
res22_1: Int = 1