스칼라 강의 (47)  ADT (Algebraic Data Types) 이란? 

위키백과: 대수적 자료형(Algebraic data type)은 다른 자료형의 값을 가지는 자료형이다. 대체로 다른 자료형을 생성자로 감싸고 있다. 어떤 값도 대수적 자료형의 생성자의 인자가 될 수 있다. 반면에 다른 자료형은 생성자를 실행할 수 없으며 형식 일치(Pattern matching) 과정을 통해 생성자를 얻을 수 있다

가장 일반적인 대수적 자료형은 두개의 생성자를 가진 목록형(list)이다. 목록형은 비어있는 목록을 위해 Nil 또는 []를 지원한다. 예를 들어 LISP에서는 Cons(생성자의 준말)나 ::, :등을 이용하여 짧은 목록을 결합하여 새로운 목록을 만들 수 있게 한다. ((Cons 1 '(2 3 4)) 또는 1:[2,3,4]와 같은 방법으로 사용된다

스칼라에서는 대략 trait 와 케이스 클래스로 ADT 를 정의 하는데 , 아래 예를 보자. 

스칼라는 아시다시피 하이브리드 함수형 언어라, 직접적으로 만들 순 없고  먼가 OO 틱하게 만든다.

Option


sealed trait Option[+A]
case object None extends Option[Nothing]
case class Some[A](a: A) extends Option[A]

Either

sealed trait Either[+A, +B]
case class Left[A](a: A) extends Either[A, Nothing]
case class Right[B](b: B) extends Either[Nothing, B]

Try


sealed trait Try[+A]
case class Success[A](a: A) extends Try[A]
case class Failure[A](t: Throwable) extends Try[A]

List


sealed trait List[+A]
case object Nil extends List[Nothing]
case class ::[A](head: A, tail: List[A]) extends List[A]

object List {
def apply[A](as: A*): List[A] = ...
}


아래 자세한 설명이 있으니 각자도생하자. 설명을 딱 부러지게 할 레베루가 안된다. 
다른곳에서 함부러 타입을 추가하지 못하게 제한을 하는 거 봐서 (sealed)  먼가 타입을 만드는데 있어서, 구조화 하고 제한 된 갯수의 타입을 만드는것 같다. (합 혹은 곱만큼) 정확한 타입은 패턴매칭을 통해서 분류 가능하고~
이런 ADT 를 이용해서 함수형 프로그래밍을 위한 복합 구성을 하는 것이고~

http://tpolecat.github.io/presentations/algebraic_types.html#1

https://gleichmann.wordpress.com/2011/01/30/functional-scala-algebraic-datatypes-enumerated-types/




ADT 와 타입클래스는 어떻게 다른가요?  - 스택오버플로우 발췌


ADT (Algebraic Data Types)와 유형 클래스는 서로 다른 문제를 해결하는 완전히 다른 개념입니다.

다음과 같이 ADT는 말 그대로 대수적 데이터 타입의 약자이구요. ADT는 데이터를 구조화하는 데 필요합니다. 스칼라에서 가장 근접한 것은 내가 생각하기에, 케이스 클래스와  sealed traits 의 조합인데 이것은 Haskell에서 도 복잡한 데이터 구조를 구성하는 주요 수단입니다. ADT의 가장 유명한 하스켈 예가 아래에 있습니다.

data Maybe a = Nothing | Just a

이 유형은 Option이라고하는 표준 스칼라 라이브러리에 직접적으로 반영 됩니다.

sealed trait Option[+T]
case class Some[T](value: T) extends Option[T]
case object None extends Option[Nothing]

이것은 Option이 표준 라이브러리에서 정의 된 방법과 정확히 같지 않지만 포인트를 집어줍니다.기본적으로 ADT는 몇 가지 명명 된 튜플 (0-ary, Nothing / None, 1-ary, Just a / Some (value))의 조합입니다.


다음 데이터 타입을 보세요.

-- Haskell
data Tree a = Leaf | Branch a (Tree a) (Tree a)
// Scala
sealed trait Tree[+T]
case object Leaf extends Tree[Nothing]
case class Branch[T](value: T, left: Tree[T], right: Tree[T]) extends Tree[T]

이것은 간단한 이진 트리입니다. 이 두 정의는 기본적으로 다음과 같이 이해 할 수 있는데요. "이진 트리는 리프 또는 분기 중 하나이며, 분기인 경우 어떤 값과 두 개의 다른 트리가 포함됩니다". 이것이 의미하는 바는 Tree 타입의 변수를 가지고 있다면 Leaf 나 Branch 중 하나를 포함 할 수 있으며, 필요하다면 어느 것이 있는지 확인하고 포함 된 데이터를 추출 할 수 있다는 겁니다. 이러한 검사와 추출의 주된 의미는 패턴 매칭입니다.

-- Haskell
showTree :: (Show a) => Tree a -> String
showTree tree = case tree of
  Leaf                    -> "a leaf"
  Branch value left right -> "a branch with value " ++ show value ++ 
                             ", left subtree (" ++ showTree left ++ ")" ++
                             ", right subtree (" ++ showTree right ++ ")"
// Scala
def showTree[T](tree: Tree[T]) = tree match {
  case Leaf => "a leaf"
  case Branch(value, left, right) => s"a branch with value $value, " +
                                     s"left subtree (${showTree(left)}), " +
                                     s"right subtree (${showTree(right)})"
}

이 개념은 매우 간단하지만 매우 강력한데요.

보시다시피 ADT는 닫힙니다. 즉, 타입을 정의한 후에 더 많은 명명된 튜플을 추가 할 수 없습니다. Haskell에서 이것은 구문적으로 시행되며, Scala에서는 다른 파일에서 하위 클래스를  정의하는 것을 허용하지 않는 sealed keyword를 통해 이 작업을 수행합니다.

그러한 이유로 이러한 타입을 algebraic 이라고합니다. 명명된 튜플은 합계(수학적 의미로도)로서 이러한 튜플의 곱 (수학적 의미에서) 및 '조합'으로 간주 될 수 있으며 이러한 생각은 심오한 이론적 의미를 갖습니다.




저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by [前草] 이승현 (wowlsh93@gmail.com)