Created
June 30, 2018 14:36
-
-
Save chrilves/2a6453bea55ddfe1d8c6f2e3466ca8e1 to your computer and use it in GitHub Desktop.
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
import scala.language.implicitConversions | |
object CHK { | |
/* Quand on sait convertir un type | |
A dans B et réciproquement | |
*/ | |
trait Conversion[A,B] { self => | |
def to: A => B | |
def from: B => A | |
final def andThen[C](c: Conversion[B,C]): Conversion[A,C] = | |
new Conversion[A,C] { | |
def to: A => C = self.to.andThen(c.to) | |
def from: C => A = c.from.andThen(self.from) | |
} | |
} | |
/* Quand on sait convertir un type | |
A dans B | |
*/ | |
trait To[-A,+B] extends (A => B) { self => | |
final def andThen[C](c: To[B,C]): To[A,C] = | |
To(self.andThen(c)) | |
} | |
object To { | |
def apply[A,B](f: A => B): To[A,B] = | |
new To[A,B] { | |
def apply(a:A): B = f(a) | |
} | |
} | |
object Conversion { | |
def id[A] = new Conversion[A,A] { | |
def to = identity[A] | |
def from = identity[A] | |
} | |
} | |
/* J'ai la fleme d'importer Shapeless | |
Mais les HList c'est plus ou moins | |
comme ça non? | |
*/ | |
sealed abstract class HList | |
final case object HNil ; type HNil = HNil.type | |
final case class HCons[+head, +tail <: HList](head: head, tail: tail) extends HList | |
/* J'ai la fleme d'importer clickhouse-scala: | |
chdriver.columns.Column | |
*/ | |
trait CHKColumn { | |
type T | |
val data: Array[T] | |
} | |
/* Le type d'une colone dont les élements sont des T | |
*/ | |
sealed abstract class MyColumn[T] { | |
/* Récupère la valeur a position index*/ | |
def get(index: Int): T | |
/* Ecrit la valeur a position index*/ | |
def put(index: Int, value: T): Unit | |
/* Convertit cette colomne en une de U */ | |
@inline final def xmap[U](conv: Conversion[T,U]): MyColumn[U] = | |
MyColumn.Converted(this, conv) | |
} | |
object MyColumn { | |
/* Une colomne native de clickhouse-scala est une MyColumn | |
*/ | |
final case class Native[_dbType]( | |
dbType : String, | |
column: CHKColumn { type T = _dbType } | |
) extends MyColumn[_dbType] { | |
type columnType = _dbType | |
/* Son get normal */ | |
def get(index: Int): _dbType = | |
column.data(index) | |
/* Et son put normal */ | |
def put(index: Int, value: _dbType): Unit = | |
column.data.update(index, value) | |
} | |
/* Comment convertir une colonne de T en colonne de U | |
Comme tu l'as fait avec Put.apply et Get.apply | |
Ca pourrait être une case class normal et pas une 'sealed abstract' | |
mais Convertir de A à B puis de B à C c'est comme convertir | |
de A à C directement. Donc au lieu de créer | |
Converted(Converted(colonneA, convAversB), convBversC) | |
Je crée directement | |
Converted(colonneA, convAversB.andThen(convBversC)) | |
*/ | |
sealed abstract case class Converted[T,U]( | |
column: MyColumn[T], | |
conversion: Conversion[T,U] | |
) extends MyColumn[U] { | |
type columnType = T | |
/* Très similaire a ton Get.apply */ | |
def get(index: Int): U = | |
conversion.to(column.get(index)) | |
/* Très similaire a ton Put.apply */ | |
def put(index: Int, value: U): Unit = | |
column.put(index, conversion.from(value)) | |
} | |
object Converted { | |
/* Un smart constructeur qui applique l'optimisation décrite plus haut */ | |
def apply[T,U](column: MyColumn[T], conversion: Conversion[T,U]): MyColumn[U] = | |
column match { | |
case Converted(col, conv) => apply(col, conv.andThen(conversion)) | |
case _ => new Converted(column, conversion) {} | |
} | |
} | |
/* Première magie, une colonne faite de HNil est simple | |
à simuler car ses valeurs ne peuvent être que des HNil | |
*/ | |
final case object MNil extends MyColumn[HNil] { | |
def get(index: Int): HNil = HNil | |
def put(index: Int, value: HNil): Unit = () | |
} | |
/* Le reste de la magie: le support de HCons */ | |
final case class MCons[head, tail <: HList]( | |
head: MyColumn[head], | |
tail: MyColumn[tail] | |
) extends MyColumn[HCons[head, tail]] { | |
def get(index: Int): HCons[head, tail] = | |
HCons(head.get(index), tail.get(index)) | |
def put(index: Int, value: HCons[head, tail]): Unit = { | |
head.put(index, value.head) | |
tail.put(index, value.tail) | |
} | |
} | |
} | |
trait DBType[T] { | |
val dbType: String | |
} | |
object DBType { | |
def apply[T](implicit ev: DBType[T]) = ev | |
def mk[T](s: String): DBType[T] = | |
new DBType[T] { | |
val dbType = s | |
} | |
} | |
sealed abstract class Get[-I,+O] { | |
def apply(index: Int, i: I): O | |
@inline final def map[X](f: O => X): Get[I, X] = | |
Get.Mapping(this, f) | |
} | |
object Get { | |
def apply[I,O](implicit ev: Get[I,O]): Get[I,O] = ev | |
final case class Native[dbType](dbType : String) extends Get[CHKColumn { type T = dbType }, dbType] { | |
type columnType = dbType | |
/* Son get normal */ | |
def apply(index: Int, column: CHKColumn { type T = dbType }): dbType = | |
column.data(index) | |
} | |
implicit def native[dbType](implicit dbType : DBType[dbType]): Get[CHKColumn { type T = dbType }, dbType] = Native(dbType.dbType) | |
sealed abstract case class Mapping[-I, O, +U](get: Get[I,O], mapping: O => U) extends Get[I, U] { | |
type middle = O | |
/* Très similaire a ton Get.apply */ | |
def apply(index: Int, i: I): U = | |
mapping(get(index, i)) | |
} | |
object Mapping { | |
/* Un smart constructeur qui applique l'optimisation décrite plus haut */ | |
def apply[I,O,U](get: Get[I,O], mapping: O => U): Get[I,U] = | |
get match { | |
case Mapping(g, m) => apply(g, m.andThen(mapping)) | |
case _ => new Mapping(get, mapping) {} | |
} | |
} | |
implicit def amapping[I,O,U](implicit get: Get[I,O], f: To[O,U]): Get[I,U] = Mapping(get, f) | |
/* Première magie, une colonne faite de HNil est simple | |
à simuler car ses valeurs ne peuvent être que des HNil | |
*/ | |
final case object MNil extends Get[HNil, HNil] { | |
def apply(index: Int, i: HNil): HNil = HNil | |
} | |
implicit val mnil: Get[HNil, HNil] = MNil | |
/* Le reste de la magie: le support de HCons */ | |
final case class MCons[-headI, +headO, -tailI <: HList, +tailO <: HList]( | |
head: Get[headI, headO], | |
tail: Get[tailI, tailO] | |
) extends Get[HCons[headI, tailI], HCons[headO, tailO]] { | |
def apply(index: Int, h: HCons[headI, tailI]): HCons[headO, tailO] = | |
HCons(head(index, h.head), tail(index, h.tail)) | |
} | |
implicit def mcons[headI, headO, tailI <: HList, tailO <: HList](implicit | |
head: Get[headI, headO], | |
tail: Get[tailI, tailO] | |
):Get[HCons[headI, tailI], HCons[headO, tailO]] = MCons(head, tail) | |
} | |
sealed abstract class Put[-I,-O] { | |
def apply(index: Int, i: I, o: O): Unit | |
@inline final def contaMap[X](f: X => O): Put[I, X] = | |
Put.Mapping(this, f) | |
} | |
object Put { | |
def apply[I,O](implicit ev: Put[I,O]): Put[I,O] = ev | |
final case class Native[dbType](dbType : String) extends Put[CHKColumn { type T = dbType }, dbType] { | |
type columnType = dbType | |
/* Son get normal */ | |
def apply(index: Int, column: CHKColumn { type T = dbType }, value: dbType): Unit = | |
column.data.update(index,value) | |
} | |
implicit def native[dbType](implicit dbType : DBType[dbType]): Put[CHKColumn { type T = dbType }, dbType] = | |
Native(dbType.dbType) | |
sealed abstract case class Mapping[-I, O, -U](put: Put[I,O], mapping: U => O) extends Put[I, U] { | |
type middle = O | |
/* Très similaire a ton Put.apply */ | |
def apply(index: Int, i: I, u: U): Unit = | |
put(index, i, mapping(u)) | |
} | |
object Mapping { | |
/* Un smart constructeur qui applique l'optimisation décrite plus haut */ | |
def apply[I,O,U](put: Put[I,O], mapping: U => O): Put[I,U] = | |
put match { | |
case Mapping(g, m) => apply(g, mapping.andThen(m)) | |
case _ => new Mapping(put, mapping) {} | |
} | |
} | |
implicit def amapping[I,O,U](implicit put: Put[I,O], f: To[U,O]): Put[I,U] = Mapping(put, f) | |
/* Première magie, une colonne faite de HNil est simple | |
à simuler car ses valeurs ne peuvent être que des HNil | |
*/ | |
final case object MNil extends Put[HNil, HNil] { | |
def apply(index: Int, i: HNil, v: HNil): Unit = () | |
} | |
implicit val mnil: Put[HNil, HNil] = MNil | |
/* Le reste de la magie: le support de HCons */ | |
final case class MCons[-headI, -headO, -tailI <: HList, -tailO <: HList]( | |
head: Put[headI, headO], | |
tail: Put[tailI, tailO] | |
) extends Put[HCons[headI, tailI], HCons[headO, tailO]] { | |
def apply(index: Int, h: HCons[headI, tailI], v: HCons[headO, tailO]): Unit = { | |
head(index, h.head, v.head) | |
tail(index, h.tail, v.tail) | |
} | |
} | |
implicit def mcons[headI, headO, tailI <: HList, tailO <: HList](implicit | |
head: Put[headI, headO], | |
tail: Put[tailI, tailO] | |
):Put[HCons[headI, tailI], HCons[headO, tailO]] = MCons(head, tail) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment