Skip to content

Instantly share code, notes, and snippets.

@chrilves
Created June 30, 2018 14:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrilves/2a6453bea55ddfe1d8c6f2e3466ca8e1 to your computer and use it in GitHub Desktop.
Save chrilves/2a6453bea55ddfe1d8c6f2e3466ca8e1 to your computer and use it in GitHub Desktop.
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