Created
April 7, 2018 13:56
-
-
Save oleg-py/f8da4e211cc8623d6c37aed6bbfc5bf9 to your computer and use it in GitHub Desktop.
Combining Ref-like type with shapeless
This file contains hidden or 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 cats._ | |
import implicits._ | |
import shapeless._ | |
import tag._ | |
import scala.language.dynamics | |
import cats.effect.IO | |
trait Var[F[_], A] extends Dynamic { outer => | |
def apply(): F[A] | |
def :=(a: A): F[Unit] | |
def ?[B](f: A => B)(implicit F: Functor[F]): F[B] = | |
this().map(f) | |
def ~= (f: A => A)(implicit F: FlatMap[F]): F[Unit] = | |
(this ? f).flatMap(this := _) | |
def selectDynamic[B](key: String)(implicit mkLens: MkFieldLens.Aux[A, Symbol @@ key.type, B], F: FlatMap[F]): Var[F, B] = | |
new Var[F, B] { | |
private[this] val lens = mkLens() | |
def apply(): F[B] = outer().map(lens.get) | |
def :=(b: B): F[Unit] = outer ~= (lens.set(_)(b)) | |
} | |
def applyDynamic[B](key: String)()(implicit mkLens: MkFieldLens.Aux[A, Symbol @@ key.type, B], F: FlatMap[F]): F[B] = | |
this().map(mkLens().get) | |
} | |
// We could build a Var on top of a lot of stuff: | |
// MonadState, fs2.Ref, monix TaskLocal, var / Atomic + Sync, etc. | |
// It could also do other stuff like syncing data with server after a while or writing to file | |
object IOVar { | |
def apply[A](seed: A): IO[Var[IO, A]] = IO { | |
new Var[IO, A] { | |
private[this] var state = seed | |
def apply(): IO[A] = IO(state) | |
def :=(a: A): IO[Unit] = IO { state = a } | |
} | |
} | |
} | |
def inc[F[_]: Monad](v: Var[F, Point], d: Double): F[Unit] = | |
for { | |
_ <- v.x ~= (_ + d) | |
_ <- v.y ~= (_ + d) | |
} yield () | |
case class Point(x: Double, y: Double) | |
case class State( | |
p1: Point = Point(0, 0), | |
p2: Point = Point(0, 0), | |
score: (Int, Int) = (0, 0), | |
set: Set[Int] = Set() | |
) | |
val program = | |
for { | |
ref <- IOVar(State()) | |
_ <- ref.p1.x := 5 | |
_ <- ref.p2.y[Double] := -5 // Explicit type params help intellij | |
_ <- ref.score._1 := 10 | |
_ <- ref.score ~= (_.swap) | |
// Pass parts of state to functions | |
_ <- inc(ref.p1, -0.5) | |
_ <- inc(ref.p2, 0.5) | |
// "Query" inner fields | |
b <- ref.set ? (_.isEmpty) | |
_ <- IO(println(s"Set is empty: $b")) | |
// Return final result | |
r <- ref() | |
} yield r | |
program.unsafeRunSync() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment