All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.chrisdavenport.rediculous.Redis.scala Maven / Gradle / Ivy

The newest version!
package io.chrisdavenport.rediculous

import cats.data._
import cats.implicits._
import cats._
import cats.effect._

final case class Redis[F[_], A](unRedis: Kleisli[F, RedisConnection[F], A]){
  // RedisConnection[F] => F[F[A]]
  def run(connection: RedisConnection[F])(implicit ev: Monad[F]): F[A] = {
    Redis.runRedis(this)(connection)
  }
}
object Redis {

  private def runRedis[F[_]: Monad, A](redis: Redis[F, A])(connection: RedisConnection[F]): F[A] = {
    redis.unRedis.run(connection)
  }

  def liftF[F[_]: Monad, A](fa: F[A]): Redis[F, A] = 
    Redis(Kleisli.liftF[F, RedisConnection[F], A](fa))

  /**
   * Newtype encoding for a `Redis` datatype that has a `cats.Applicative`
   * capable of doing parallel processing in `ap` and `map2`, needed
   * for implementing `cats.Parallel`.
   *
   * Helpers are provided for converting back and forth in `Par.apply`
   * for wrapping any `Redis` value and `Par.unwrap` for unwrapping.
   *
   * The encoding is based on the "newtypes" project by
   * Alexander Konovalov, chosen because it's devoid of boxing issues and
   * a good choice until opaque types will land in Scala.
   * [[https://github.com/alexknvl/newtypes alexknvl/newtypes]].
   *
   */
  type Par[+F[_], +A] = Par.Type[F, A]

  object Par {

    type Base
    trait Tag extends Any
    type Type[+F[_], +A] <: Base with Tag

    def apply[F[_], A](fa: Redis[F, A]): Type[F, A] =
      fa.asInstanceOf[Type[F, A]]

    def unwrap[F[_], A](fa: Type[F, A]): Redis[F, A] =
      fa.asInstanceOf[Redis[F, A]]

    def parallel[F[_]]: Redis[F, *] ~> Par[F, *] = new ~>[Redis[F, *], Par[F, *]]{
      def apply[A](fa: Redis[F,A]): Par[F,A] = Par(fa)
    }
    def sequential[F[_]]: Par[F, *] ~> Redis[F, *] = new ~>[Par[F, *], Redis[F, *]]{
      def apply[A](fa: Par[F,A]): Redis[F,A] = unwrap(fa)
    }

    implicit def parApplicative[F[_]: Parallel: Monad]: Applicative[Par[F, *]] = new Applicative[Par[F, *]]{
      def ap[A, B](ff: Par[F,A => B])(fa: Par[F,A]): Par[F,B] = Par(Redis{
        val kfx : Kleisli[F, RedisConnection[F], A => B] = Par.unwrap(ff).unRedis
        val kfa : Kleisli[F, RedisConnection[F], A] = Par.unwrap(fa).unRedis
        kfx.parAp(kfa)
      })
      def pure[A](x: A): Par[F,A] = Par(Redis(
        Kleisli.pure[F, RedisConnection[F], A](x)
      ))
    }

  }

  implicit def monad[F[_]: Monad]: Monad[Redis[F, *]] = new Monad[Redis[F, *]]{

    def tailRecM[A, B](a: A)(f: A => Redis[F,Either[A,B]]): Redis[F,B] = Redis(
      Monad[Kleisli[F, RedisConnection[F], *]].tailRecM[A, B](a)(f.andThen(_.unRedis.flatMap(fe => Kleisli.liftF(fe.pure[F]))))
    )

    def flatMap[A, B](fa: Redis[F,A])(f: A => Redis[F,B]): Redis[F,B] = Redis(
      fa.unRedis.flatMap(f(_).unRedis)
    )

    
    def pure[A](x: A): Redis[F, A] = Redis(
      Kleisli.pure[F, RedisConnection[F], A](x)
    )
  }

  implicit def parRedis[M[_]: Parallel: Concurrent]: Parallel[Redis[M, *]] = new Parallel[Redis[M, *]]{
    
    type F[A] = Par[M, A]
    
    def sequential: F ~> Redis[M, *] =
      Par.sequential[M]
    
    def parallel: Redis[M, *] ~> F = Par.parallel[M]
    
    def applicative: Applicative[F] = Par.parApplicative[M] 
    
    def monad: Monad[Redis[M, *]] = Redis.monad[M]
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy