Slide 1

Slide 1 text

shapelessと代数的データ型

Slide 2

Slide 2 text

本⽇の内容 shapelessの使い⽅を追っている時に、代数的データ型について調べる機会があったので、ま とめました。 . shapelessとは? . 代数的データ型(Alberic Data Type; ADT)とは? i. 直積型(product type) ii. 直和型(sum type) . shapelessと代数的データ型

Slide 3

Slide 3 text

shapelessって?

Slide 4

Slide 4 text

shapelessって? 型クラスと依存型をもとにした、ジェネリックプログラミングをScalaで実現するためのライ ブラリ。 shapelessって?

Slide 5

Slide 5 text

ジェネリックプログラミング 似たような構造を持つ型を抽象化することで、コードの再利⽤性を⾼つつ、型安全を保証さ せる⼿法。 shapelessって?

Slide 6

Slide 6 text

ジェネリック型 List("hoge", "fuga", "piyo") # ⽂字列のリスト List(1, 2, 3) # 数値のリスト どちらも要素が連なっているという構造なので、要素となっている型をパラメーター化して List[T] とする。 shapelessって?

Slide 7

Slide 7 text

ジェネリックメソッド def max(x: Int, y: Int): Int = { if (x > y) x else y } def max(x: Float, y: Float): Float = { if (x > y) x else y } このように扱う引数や返り値の型が違うだけで、アルゴリズムが同じ関数は以下のように抽 象化する。 def max(x: T, y: T): T = { if (x > y) x else y } shapelessって?

Slide 8

Slide 8 text

代数的データ型 代数的データ型

Slide 9

Slide 9 text

代数的データ型(Algebratic Data Type; ADT) 関数型プログラミングで⽤いられている概念 andの意味を持つ型とorの意味を持つ型を使って表現されるデータ型 代数的データ型

Slide 10

Slide 10 text

andの意味を持つ型 product type(tuple; record) ⽇本語では直積型 「AとB」のように複数のデータの組み合わせで表すデータ型 代数的データ型

Slide 11

Slide 11 text

例: 座標 2次元上の座標をproduct typeで表す Scala case class Point(x: Int, y: Int) type Point = (Int, Int) Haskell data Point = Point Int Int 代数的データ型

Slide 12

Slide 12 text

orの意味を持つ型 sum type(tagged; disjoint union; coproduct; variant) ⽇本語では直和型 「AかB」のように複数のデータの組み合わせで表すデータ型 AやBの値 代数的データ型

Slide 13

Slide 13 text

例: 図形 半径を持つ円と底辺・⾼さを持つ⻑⽅形を図形としてsum typeで表す sealed trait Shape case class Circle(raidus: Double) extends Shape case class Rectangle(width: Double, Double) extends Shape type Circle = Double type Rectangle = (Double, Double) type Shape = Either[Either[Rectangle, Circle], Triangle] // Scala3 enum Shape: case Circle(radius: Int) case Rectangle(width: Int, height: Int) data Shape = Circle Double | Rectangle Double Double 代数的データ型

Slide 14

Slide 14 text

列挙型は直和型なのか? 例えば、⾚・⻩・⻘⾊を持つ列挙型 sealed trait Color case object Red extends Color case object Yellow extends Color case object Blue extends Color この場合、コンストラクタが引数を取らないので、直和型とは⾔えない。 代数的データ型

Slide 15

Slide 15 text

shapelessと代数的データ型 shapelessと代数的データ型

Slide 16

Slide 16 text

shapelessの機能の⼀つにheterogeneous listを使って、ADTとジェネリックな型を⾏き来す る機能がある。 heterogeneous list: 異質の, 異成分からなる 代数的データ型(ADT) case class Point(x: Int, y: Int) trait Shape case class Circle(redion: Int) extends Shape case class Rectangle(width: Int, height: Int): extends Shape ジェネリックな型 Heterogeneous List Int :: Int :: HNil type Point = (Int, Int) type Circle = (Int) type Rectangle = (Int, Int) type Shape = Either[Circle, Rectangle] shapeless shapelessと代数的データ型

Slide 17

Slide 17 text

例えば、Scalaでは以下のようなListは作れない List(1, "hoge", true) 1::"hoge"::true::Nil ※ 正確には List[Any] と定義することでできるけど... shapelessと代数的データ型

Slide 18

Slide 18 text

そこで、Heterogeneous Listを使って「Int型とString型とBoolean型が組み合わさった型」を 定義することができる。 val x: Int :: String :: Boolean :: HNil = 1::"hoge"::true::HNil shapelessと代数的データ型

Slide 19

Slide 19 text

例えば、直積型の場合は case class Hoge(foo: Int, bar: String, baz: Boolean) val gen = Generic[Hoge] gen.to(Hoge(1, "fuga", true)) //res: gen.Repr = 1 :: "fuga" :: true ::HNil gen.from(1::"fuga"::true) //res: Hoge = Hoge(1, "fuga", true) で、ADTとHeterogeneous Listを⾏き来することができる。 shapelessと代数的データ型

Slide 20

Slide 20 text

また、直和型の場合は sealed trait MetaSyntax final case class Hoge(fuga: Int, piyo: String) extends MetaSyntax final case class Foo(bar: String, baz: Boolean) extends MetaSyntax val gen = Generic[MetaSyntax] gen.to(Hoge(1, "piyo")) //res: gen.Repr = Inr(Inl(Hoge(1,piyo))) で、変換することができる。 shapelessと代数的データ型

Slide 21

Slide 21 text

このADTとジェネリックな型を⾏き来する機能を利⽤する場⾯として、例えば . EntityとDTOの変換 . 似たデータ構造を受け取り、変換する関数の実装 shapelessと代数的データ型

Slide 22

Slide 22 text

EntityとDTOの変換 DBとアプリケーションの間でデータのやりとりをするときにEntityとは別にDTOを使ってい る場合に、EntityとDTOを変換する時に使える。 case class HogeEntity(foo: Int, bar: String, baz: Boolean) case class HogeDTO(foo: Int, bar: String, baz: Boolean) def getRepr[A](value: A)(implicit gen: Generic[A]) = gen.to(value) def toEntity(dto: HogeDTO): HogeEntity = Generic[HogeEntity].from(getRepr(dto)) def toDto(entity: HogeEntity): HogeDTO = Generic[HogeDTO].from(getRepr(entity)) toEntity(HogeDTO(1, "fuga", true)) //res: HogeEntity = HogeEntity(1, "fuga", true) toDto(HogeEntity(2, "piyo", false)) //res: HogeDto = HogeDto(2, "piyo", false) shapelessと代数的データ型

Slide 23

Slide 23 text

似たデータの構造を受け取り、変換する関数の実装 例えば、同じデータ構造のclassからCSV形式の⽂字列に変換する関数を以下のように実装で きる。 case class HogeEntity(foo: Int, bar: String, baz: Boolean) case class HogeDTO(foo: Int, bar: String, baz: Boolean) def getRepr[A](value: A)(implicit gen: Generic[A]) = gen.to(value) def toCsv(row: Int :: String :: Boolean :: HNil): List[String] = List(row(0).toString, row(1), row(2).toString) val genEntity = getRepr(HogeEntity(1, "fuga", true)) toCsv(genEntity) //res: List[String] = List("1", "fuga", "true") val genDto = getRepr(HogeDTO(2, "piyo", false)) toCsv(genDto) //res: List[String] = List("2", "piyo", "false") shapelessと代数的データ型

Slide 24

Slide 24 text

参考⽂献 Guide to Shapeless Qiita 「ADT, 直和・直積, State Machine」 Haskell wiki「Algebraic data type」 shapelessと代数的データ型