Refactor Storage to make sense in the context of external querylangs (Quill)

This commit is contained in:
Aly 2019-01-13 20:52:27 -08:00
parent b52159a915
commit bd78f6eee3
No known key found for this signature in database
GPG Key ID: 555B7346639DDAC3
7 changed files with 81 additions and 54 deletions

View File

@ -18,7 +18,9 @@ val sharedSettings = Seq(
"org.typelevel" %%% "cats-core" % "1.5.0",
"org.typelevel" %%% "cats-effect" % "1.1.0",
"org.scala-graph" %%% "graph-core" % "1.12.5",
)
),
resolvers += Resolver.sonatypeRepo("releases"),
addCompilerPlugin("org.spire-math" % "kind-projector" % "0.9.8" cross CrossVersion.binary)
)
lazy val cataquack =

View File

@ -0,0 +1,29 @@
package tf.bug.cataquack
import cats.Functor
import cats.effect.IO
/**
* A case class meant to be used under IORaw (see package.scala) like so:
* {{{
* type StringIORaw[T] = IORaw[T, String]
* }}}
* @param s A concrete value that has been retrieved
* @tparam T The target type
* @tparam S The raw type
*/
case class Raw[S, T](s: S) {
def read(implicit readEv: Read[S, T]): T = readEv.apply(s)
def map[U](f: T => U): Raw[S, U] = Raw[S, U](s)
}
trait RawImplicits {
implicit def rawFunctor[S]: Functor[Raw[S, ?]] = new Functor[Raw[S, ?]] {
override def map[A, B](fa: Raw[S, A])(f: A => B): Raw[S, B] = fa.map(f)
}
}

View File

@ -1,9 +1,20 @@
package tf.bug.cataquack
import cats.Functor
import cats._
import cats.data._
import cats.implicits._
trait Storage[F[_], T] {
trait Storage[F[_], C[_]] {
def get(key: String): F[T]
def query[T](key: String): F[T]
def map[A, B](fa: F[A])(f: A => B)(implicit functor: Functor[F]): F[B] = fa.map(f)
def flatMap[A, B](fa: F[A])(f: A => F[B])(implicit flatMap: FlatMap[F]): F[B] = fa.flatMap(f)
def filter[T](ft: F[T])(f: T => Boolean)(implicit filterFunctor: FunctorFilter[F]): F[T] = ft.filter(f)
/** Type like List, Id, etc */
type Output[T]
def execute[T: C](f: F[T]): this.Output[T]
}

View File

@ -1,3 +1,3 @@
package tf.bug.cataquack
object implicits extends ReadImplicits
object implicits extends ReadImplicits with RawImplicits

View File

@ -0,0 +1,9 @@
package tf.bug
import cats.effect.IO
package object cataquack {
type IORaw[S, T] = IO[Raw[S, T]]
}

View File

@ -1,39 +0,0 @@
package tf.bug.cats
import cats._
import cats.data._
import cats.implicits._
import scalax.collection.Graph
import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._
import tf.bug.cataquack.{Read, Storage}
case class IDCat(id: Int, name: String, friends: Set[Int]) {
def asNode: CatNode = CatNode(id, name)
}
case class CatNode(id: Int, name: String)
object Cat {
def getCats[F[_]: Functor, T](s: Storage[F, T])(implicit read: Read[T, Vector[IDCat]]): F[Vector[IDCat]] = {
s.get("cats").map(read.apply)
}
def getCatGraphFromVec[F[_]: Functor, T](s: Storage[F, T])(implicit read: Read[T, Vector[IDCat]]): F[Graph[CatNode, UnDiEdge]] = {
getCats(s).map(cats => {
val emptyGraph = Graph.empty[CatNode, UnDiEdge]
cats.foldLeft(emptyGraph)((g, nc) => g ++ nc.friends.map(friendID => {
val fc = cats.find(_.id == friendID)
fc.map(rdc => {
val ncd = nc.asNode
val nrd = rdc.asNode
ncd ~ nrd
})
}).filter(_.isDefined).map(_.get))
})
}
def getCatGraph[F[_]: Functor, T](s: Storage[F, T])(implicit read: Read[T, Graph[CatNode, UnDiEdge]]): F[Graph[CatNode, UnDiEdge]] = {
s.get("cats").map(read.apply)
}
}

View File

@ -6,41 +6,56 @@ import cats._
import cats.data._
import cats.implicits._
import cats.effect.IO
import tf.bug.cataquack.{Read, Storage}
import tf.bug.cataquack.{IORaw, Raw, Read, Storage}
import tf.bug.cataquack.implicits._
import scala.io.Source
object Example {
type StringIORaw[T] = IORaw[String, T]
type ReadString[T] = Read[String, T]
def main(args: Array[String]): Unit = {
val f: File = new File("counter.txt")
val s: Storage[IO, String] = (key: String) => IO {
Source.fromFile(f).getLines().find(_.startsWith(key ++ " ")) match {
case Some(line) => line.drop(key.length + 1)
case None => throw new IllegalArgumentException(s"No such key: $key")
val s: Storage[StringIORaw, ReadString] = new Storage[StringIORaw, ReadString] {
override def query[T](key: String): StringIORaw[T] = IO {
val s = Source.fromFile(f).getLines()
s.find(_.startsWith(key ++ " ")) match {
case Some(l) => Raw[String, T](l.drop(key.length + 1))
case None => throw new IllegalArgumentException(s"No such key: $key")
}
}
/** Type like IO[List], Id, etc */
override type Output[T] = IO[T]
override def execute[T: ReadString](f: StringIORaw[T]): Output[T] = f.map(_.read)
}
val count = readCount(s)
val desc = readDescription(s)
val pr = for {
c <- count
d <- desc
} yield println(s"$d: $c")
} yield println(s"${d.read}: ${c.read}")
pr.unsafeRunSync()
}
case class Counter(n: Long)
case class Description(n: String)
def readCount[F[_]: Functor, T](s: Storage[F, T])(implicit read: Read[T, Long]): F[Long] = {
s.get("count").map(read.apply)
def readCount[F[_]: Functor, C[_]](s: Storage[F, C]): F[Long] = {
s.query[Long]("count")
}
def readDescription[F[_]: Functor, T](s: Storage[F, T])(implicit read: Read[T, String]): F[String] = {
s.get("desc").map(read.apply)
def readDescription[F[_]: Functor, C[_]](s: Storage[F, C]): F[String] = {
s.query[String]("desc")
}
implicit def readStringLong: Read[String, Long] = (i: String) => i.toLong
implicit def stringIORawFunctor: Functor[StringIORaw] = new Functor[StringIORaw] {
override def map[A, B](fa: StringIORaw[A])(f: A => B): StringIORaw[B] = fa.map(a => rawFunctor[String].map(a)(f))
}
}