Skip to content

Instantly share code, notes, and snippets.

@rirakkumya
Created February 14, 2013 12:19
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rirakkumya/4952429 to your computer and use it in GitHub Desktop.
Save rirakkumya/4952429 to your computer and use it in GitHub Desktop.
「衝撃的なデータベース理論・関手的データモデル」をscalaで実装してみた
// ネタ元:
// 衝撃的なデータベース理論・関手的データモデル 入門
// 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