Created
February 14, 2013 12:19
-
-
Save rirakkumya/4952429 to your computer and use it in GitHub Desktop.
「衝撃的なデータベース理論・関手的データモデル」をscalaで実装してみた
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ネタ元: | |
// 衝撃的なデータベース理論・関手的データモデル 入門 | |
// http://d.hatena.ne.jp/m-hiyama/20130211/1360577040 | |
//データ保持用 | |
object Strage { | |
trait Strage[A] { | |
def get: A | |
} | |
} | |
object Book { | |
//case classで定義しているけど、Bookは単なるテーブルのラベルで、実体はなんでも良い | |
case class Book(isbn: Int) | |
//関数がカラム | |
def isbn: Book => Int = _.isbn | |
} | |
//例えば下記のようにCSVとして扱ってもOK | |
trait BookT { | |
type Book | |
def isbn: Book => Int | |
} | |
object CsvBook extends BookT { | |
type Book = String | |
def isbn: Book => Int = b => b.split(",")(0).toInt | |
} | |
//購入者情報 | |
object Person { | |
import Strage._ | |
type YMD = (String, String, String) | |
case class Person(num: Int, name: String, birth: YMD) | |
def num: Person => Int = _.num | |
//データは関手じゃなきゃダメなのでこの実装は良くなさそうだけど、とりあえずこれで | |
def num_1[A <: Map[Int, Person]: Strage]: Int => Person = implicitly[Strage[A]].get | |
def birth: Person => YMD = _.birth | |
def name: Person => String = _.name | |
} | |
//購入情報 | |
object Purchase { | |
import java.util.Date | |
import Person._ | |
import Book._ | |
import Strage._ | |
case class Purchase(book: Book, personNum: Int, dateTime: Date) | |
def personNum: Purchase => Int = _.personNum | |
def person[A <: Map[Int, Person]: Strage]: Purchase => Person = num_1 compose personNum | |
def dateTime: Purchase => Date = _.dateTime | |
} | |
//データ定義 | |
object StrageImpl { | |
import Strage._ | |
private lazy val data = Map( | |
1 -> Person.Person(1, "foo", ("2000", "01", "01")), | |
2 -> Person.Person(2, "bar", ("2010", "01", "01")), | |
3 -> Person.Person(3, "foobar", ("2013", "01", "01"))) | |
//本来は永続ストレージ等から取ってくる | |
implicit val psersonDb = new Strage[Map[Int, Person.Person]] { | |
def get = data | |
} | |
} | |
object FunctorialDataModel { | |
import java.text.SimpleDateFormat | |
import java.util.Date | |
import java.util.Calendar | |
import StrageImpl._ | |
//例題にあるパスを実装してみる | |
//関数合成で[Purchase, person, Person, birth, YMD, calcAge, Integer]のパスを作成 | |
def path1: Purchase.Purchase => Int = calcAge compose Person.birth compose Purchase.person | |
//関数合成で [Integer, mum~1, Person, name, String]のパスを作成 | |
def path2: Int => String = Person.name compose Person.num_1 | |
private lazy val DATE_FORMAT = new SimpleDateFormat("yyyy/mm/dd") | |
def calcAge: Person.YMD => Int = { ymd => | |
val currentDate = Calendar.getInstance(); | |
val targetDate = Calendar.getInstance() | |
targetDate.setTime(DATE_FORMAT.parse(s"${ymd._1}/${ymd._2}/${ymd._3}")) | |
val age = currentDate.get(Calendar.YEAR) - targetDate.get(Calendar.YEAR) | |
age | |
} | |
//スキーマ圏をデータ圏に持ち上げる。必要無いけど関手的って名前の元なので書いとく | |
def schema2Data[A, B](f: A => B): Seq[A] => Seq[B] = _ map f | |
} | |
object Exec extends App { | |
import java.util.Date | |
import FunctorialDataModel._ | |
//購入情報から購入者の年齢を取得 | |
val purchaseData = (1 to 3) map { x => | |
Purchase.Purchase(Book.Book(x * 100), x, new Date()) | |
} | |
schema2Data(path1)(purchaseData) foreach println | |
//購入者番号から購入者名を取得 | |
val personalNums = (1 to 3) | |
schema2Data(path2)(personalNums) foreach println | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment