これがわかるとshapelessのコードも読めるかもしれない

25
これが分かればshapelessのコードも 読めるかもしれない Shapeless勉強会 2016/03/27 Yasuki Okumura

Upload: yasuki-okumura

Post on 09-Jan-2017

456 views

Category:

Software


1 download

TRANSCRIPT

これが分かればshapelessのコードも読めるかもしれない

Shapeless勉強会 2016/03/27

Yasuki Okumura

Who?

● 教育学部数学科 出身

● @busterdayo● サーバーサイドエンジニア at Tamecco

Demo

Agenda

● ペアノの公理

● 自然数 - オブジェクト

● 自然数 + 加法 - オブジェクト

● 自然数 + 加法 - 型● 応用例 - N次元ベクトル

● Def macro● Dynamic

ペアノの公理 - wikipedia調べ

自然数は次の5条件を満たす。

● 自然数 0 が存在する。

● 任意の自然数 a にはその後者 (successor)、suc(a) が存在する(suc(a) は a + 1 の "意味")。

● 0 はいかなる自然数の後者でもない(0 より前の自然数は存在しない)。

● 異なる自然数は異なる後者を持つ:a ≠ b のとき suc(a) ≠ suc(b) となる。

● 0 がある性質を満たし、a がある性質を満たせばその後者 suc(a) もその性質を満

たすとき、すべての自然数はその性質を満たす。

https://ja.wikipedia.org/wiki/ペアノの公理

自然数 - オブジェクト

sealed trait 自然数

object 零 extends 自然数

case class 後者(a: 自然数) extends 自然数

Demo

自然数 + 加法 - オブジェクト

自然数の加法は再帰的に、以下のように定義できる。

● すべての自然数 a に対して、a + 0 = a● すべての自然数 a, b に対して、a + suc(b) = suc(a + b)https://ja.wikipedia.org/wiki/自然数

● すべての自然数 a に対して、零 + a = a● すべての自然数 a, b に対して、後者(b) + a = 後者(a + b)

自然数 + 加法 - オブジェクト

sealed trait 自然数 { def +( n: 自然数 ) : 自然数

}object 零 extends 自然数 { def +( a: 自然数 ): 自然数 = a // 零 + a = a}case class 後者(b: 自然数) extends 自然数 { def +( a: 自然数 ): 自然数 = 後者( a + b ) // 後者(b) + a = 後者(a + b)

}

Demo

証明: 0 + a = a ------> 公理1後者(a) + b = 後者(a + b) ------> 公理2は公理として扱う。(証明なしで正しいものとして扱う)

以下が正しいことを証明する。

a + 0 = a → n1a + 後者(b) = 後者(a + b) -> n2----数学的帰納法 を使う

n1の証明

* a = 0 の時

0 + 0 = 0 -----> 公理1 より

* a = k の時

k + 0 = k が成り立つと仮定 ---> 仮定1* a = 後者(k)後者(k) + 0 = 後者(k + 0) -----> 公理2より

= 後者(k) -----> 仮定1 より

n2 の証明:* a = 0の時0 + 後者(b) = 後者(b) ---> 公理2より = 後者(0 + b) ---> 公理1より

* a = k の時k + 後者(b) = 後者(k + b) → 仮定2

* a = 後者(k)の時後者(k) + 後者(b) = 後者(k + 後者(b)) = 後者(後者(k + b)) ---> 仮定2 = 後者(後者(k) + b) ---> 公理2

1 + 1 = 2は当たり前?

object 零 extends 自然数 { def +( a: 自然数 ): 自然数 = a}case class 後者(b: 自然数) extends 自然数 { def +( a: 自然数 ): 自然数 = 後者( a + b ) }

object 零 extends 自然数 { def +( a: 自然数 ): 自然数 = a}case class 後者(b: 自然数) extends 自然数 { def +( a: 自然数 ): 自然数 = a match { case 後者(c) => 後者(c +b) case 零 => 後者(b) }}

代数学 - 群,環,体

*http://d.hatena.ne.jp/Zellij/20121211/p1

Swiftで代数学http://qiita.com/taketo1024/items/bd356c59dc0559ee9a0b

自然数 + 加法 - 型

sealed trait 自然数 { def +( n: 自然数 ) : 自然数

}object 零 extends 自然数 { def +( a: 自然数 ): 自然数 = a }case class 後者(b: 自然数) extends

自然数 { def +( a: 自然数 ): 自然数 = 後者( a + b ) }

sealed trait 自然数 { type 足す[N <: 自然数] <: 自然数

}trait 零 extends 自然数{ override type 足す[N <: 自然数] = N}trait 後者[N<:自然数] extends 自然数 { override type 足す[O <: 自然数] = 後者[N#足す[O]]}

応用例 - N次元ベクトル trait

sealed trait ベクトル集合[次元数 <: 自然数] { def +( vector: ベクトル集合[次元数] ): ベクトル集合[次元数] def ++[任意の次元数 <: 自然数]( vector: ベクトル集合[任意の次元数] ): ベクトル集合[次元数#足す[任意の次元数]]}

N次元ベクトル - 零次元ベクトル

case object 零次元ベクトル extends ベクトル集合 [零] { def +( vector: ベクトル集合 [零] ): ベクトル集合 [零] = 零次元ベクトル

def ++[任意の次元数 <: 自然数]( vector: ベクトル集合 [任意の次元数] ): ベクトル集合 [零#足す[任意の次元数]] = vector}

N次元ベクトル - 一次元以上

case class ベクトル[残り個数 <: 自然数] ( 最初: Int, 残り: ベクトル集合 [残り個数] ) extends ベクトル集合 [後者[残り個数]] { def +( vector: ベクトル集合 [後者[残り個数]] ): ベクトル集合 [後者[残り個数]] = { vector match { case ベクトル( head, tail ) => ベクトル[残り個数]( 最初 + head, 残り + tail) } } def ++[Size <: 自然数]( vector: ベクトル集合 [Size] ): ベクトル集合 [後者[残り個数]#足す[Size]] = new ベクトル[残り個数#足す[Size]]( 最初, 残り.++( vector ) )}

デモ

def macroやりたかったこと。

ベクトル(1,2,3,4,5)

ベクトル(1, ベクトル(2, ベクトル(3, ベクトル(4, ベクトル(5, 零次元ベクトル)))))

Def macro def apply[次元数 <: 自然数]( numbers: Int* ): ベクトル集合[次元数] = macro ベクトルマクロ実装[次元数]

def ベクトルマクロ実装[個数 <: 自然数]( c: Context )(numbers: c.Expr[Int]*) = { import c.universe._ val succTpe = typeOf[後者[_]].typeConstructor val _0Tpe = typeOf[零] if ( numbers.isEmpty ) c.abort( c.enclosingPosition, "Need more than or equl to 1 Integer!!!" ) (numbers.foldRight((_0Tpe,q"""_root_.com.github.buster84.零次元ベクトル""": Tree)){ case ( literal, (numTpe, accTree )) => (appliedType(succTpe, numTpe), q"""new _root_.com.github.buster84.ベクトル[${numTpe}]( ${literal}, ${accTree})""") })._2 }

Dynamic - applyDynamicclass MyArray extends Dynamic { val a = Array("a1", "a2", "a3") val b = Array("b", "bb", "bbb") def applyDynamic(key: String)(index: Int): String = { key match { case "aAt" => a(index) case "bAt" => b(index) case _ => "" } }}

scala> val arr = new MyArrayarr: MyArray = MyArray@19e2223

scala> arr.aAt(1)res29: String = a2

scala> arr.bAt(1)res30: String = bb

scala> arr.zAt(0)res31: String = ""

* 参考: http://www.ne.jp/asahi/hishidama/home/tech/scala/dynamic.html

Dynamic - applyDynamicやりたかったこと。

ベクトル.create2(1,2)

ベクトル.apply[後者[後者[零]]](1, 2)

Dynamic - applyDynamic + macrodef applyDynamic(method: String)(numbers: Int*): Any = macro forwardToApplydef forwardToApply(c: Context)(method: c.Expr[String])(numbers: c.Expr[Int]*) = {

import c.universe._ val succSym = typeOf[後者[_]].typeConstructor.typeSymbol val _0Sym = typeOf[_0].typeSymbol def loop(i: Int, acc: Tree): Tree = { if(i == 0) acc else loop(i-1, AppliedTypeTree(Ident(succSym), List(acc))) }

val numTypeTree = loop(size, Ident(_0Sym)) val numbersTree = numbers.map(_.tree).toList Apply(TypeApply(Select(Ident(TermName("ベクトル")), TermName("apply")), List(numTypeTree)), numbersTree)

}https://github.com/buster84/Natural/blob/master/src/main/scala/com/github/buster84/ベクトル.scala

Demo

References

https://github.com/milessabin/shapeless

https://www.parleys.com/tutorial/type-level-programming-scala-101

http://downloads.typesafe.com/website/presentations/ScalaDaysSF2015/T4_Barnes_Typelevel_Prog.pdf

https://ja.wikipedia.org/wiki/自然数