Skip to content

Instantly share code, notes, and snippets.

@oleg-py
Created April 7, 2018 13:56
Show Gist options
  • Save oleg-py/f8da4e211cc8623d6c37aed6bbfc5bf9 to your computer and use it in GitHub Desktop.
Save oleg-py/f8da4e211cc8623d6c37aed6bbfc5bf9 to your computer and use it in GitHub Desktop.
Combining Ref-like type with shapeless
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