Skip to content
Permalink
Browse files

More ergonomics (#141)

* promise mapper refactorings

* Client and Handler traits in core module + Functor defined, better send ergonomics (arities boilerplate gen), minor rework on fs2 test

* missed CLI

* diverging implicit expansion hack

* remove ConcurrentEffect
  • Loading branch information...
sirocchj committed Sep 18, 2019
1 parent 66c6f1b commit 72b0503ecd95d79c7bef6c4b308b6eb9b00d9316
@@ -6,17 +6,18 @@ import java.util.concurrent.Executors._
import _root_.fs2.{io, text}
import cats.effect._
import cats.syntax.all._
import laserdisc.fs2._
import log.effect.fs2.Fs2LogWriter

import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContext.fromExecutorService
import scala.reflect.runtime.universe
import scala.tools.reflect.ToolBox

import laserdisc.fs2._

object CLI extends IOApp.WithContext { self =>

private[this] implicit final val concurrent: Concurrent[IO] = IO.ioConcurrentEffect

private[this] final val logo =
"""
| ./oydmNmmmmmmmmmmmmmmmmmmNmdyo/-
@@ -133,7 +134,7 @@ object CLI extends IOApp.WithContext { self =>
.evalMap { protocol =>
for {
startTime <- IO(System.nanoTime())
maybeProtocolResponse <- redisClient.send1(protocol.asInstanceOf[Protocol.Aux[protocol.A]])
maybeProtocolResponse <- redisClient.send(protocol.asInstanceOf[Protocol.Aux[protocol.A]])
endTime <- IO(System.nanoTime())
} yield maybeProtocolResponse.asInstanceOf[Maybe[Any]] -> (endTime - startTime)
}
@@ -0,0 +1,26 @@
package laserdisc

trait ClientExt[F[_], Env] { this: Client[F, Env] =>
import shapeless._
import shapeless.ops.hlist.Tupler

import scala.concurrent.duration.FiniteDuration

[2..#final def send[[#A1#]]([#
protocolA1: Protocol.Aux[A1]#],
timeout: FiniteDuration
)(
implicit F: Functor[F],
ev##0: Handler.Aux[F, Env, [#Protocol.Aux[A1]# :: ] :: HNil, [#Maybe[A1]# :: ] :: HNil],
ev##1: Tupler[[#Maybe[A1]# :: ] :: HNil]
): F[ev##1.Out] = F.map(send([#protocolA1# :: ] :: HNil, timeout))(_.tupled)
final def send[[#A1#]]([#
protocolA1: Protocol.Aux[A1]#]
)(
implicit F: Functor[F],
ev##0: Handler.Aux[F, Env, [#Protocol.Aux[A1]# :: ] :: HNil, [#Maybe[A1]# :: ] :: HNil],
ev##1: Tupler[[#Maybe[A1]# :: ] :: HNil]
): F[ev##1.Out] = F.map(send([#protocolA1# :: ] :: HNil, defaultTimeout))(_.tupled)#

]
}
@@ -0,0 +1,5 @@
package laserdisc

trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
@@ -0,0 +1,61 @@
package laserdisc

import shapeless.ops.hlist.ToSized
import shapeless.ops.sized.ToHList
import shapeless._

import scala.annotation.implicitNotFound
import scala.collection.LinearSeq
import scala.concurrent.duration._

trait ClientBase[F[_], Env] {
def defaultTimeout: FiniteDuration = 20.seconds

def send[In <: HList, Out <: HList](in: In, timeout: FiniteDuration)(implicit handler: Handler.Aux[F, Env, In, Out]): F[Out]
final def send[In <: HList, Out <: HList](in: In)(implicit ev: Handler.Aux[F, Env, In, Out]): F[Out] = send(in, defaultTimeout)

final def send[A1](protocolA1: Protocol.Aux[A1], timeout: FiniteDuration)(
implicit F: Functor[F],
ev: Handler.Aux[F, Env, Protocol.Aux[A1] :: HNil, Maybe[A1] :: HNil]
): F[Maybe[A1]] = F.map(send(protocolA1 :: HNil, timeout))(_.head)
final def send[A1](protocolA1: Protocol.Aux[A1])(
implicit F: Functor[F],
ev: Handler.Aux[F, Env, Protocol.Aux[A1] :: HNil, Maybe[A1] :: HNil]
): F[Maybe[A1]] = send(protocolA1, defaultTimeout)

final def send[CC[x] <: LinearSeq[x], A, N <: Nat, In <: HList, Out <: HList](
sizedSeq: Sized[CC[Protocol.Aux[A]], N],
timeout: FiniteDuration
)(
implicit F: Functor[F],
toHList: ToHList.Aux[CC[Protocol.Aux[A]], N, In],
ev0: Handler.Aux[F, Env, In, Out],
ev1: ToSized.Aux[Out, CC, Maybe[A], N]
): F[Sized[CC[Maybe[A]], N]] = F.map(send(toHList(sizedSeq), timeout))(_.toSized)
final def send[CC[x] <: LinearSeq[x], A, N <: Nat, In <: HList, Out <: HList](sizedSeq: Sized[CC[Protocol.Aux[A]], N])(
implicit F: Functor[F],
toHList: ToHList.Aux[CC[Protocol.Aux[A]], N, In],
ev0: Handler.Aux[F, Env, In, Out],
ev1: ToSized.Aux[Out, CC, Maybe[A], N]
): F[Sized[CC[Maybe[A]], N]] = send(sizedSeq, defaultTimeout)
}

trait Client[F[_], Env] extends ClientBase[F, Env] with ClientExt[F, Env]

trait Handler[F[_], Env, In <: HList] extends DepFn2[Env, In] {
override final type Out = F[LOut]
type LOut <: HList
}

object Handler {
@implicitNotFound(
"""Cannot derive Handler[${F}, ${Env}, ${In}] { type Out = ${LOut0} }
This could depend on many things but most likely:
- ${In} is not an HList of only laserdisc.Protocol types
- deriving this Handler requires other type classes to be available in implicit scope
Try running scalac with -Xlog-implicits (or https://github.com/tek/splain)
"""
) type Aux[F[_], Env, In <: HList, LOut0 <: HList] = Handler[F, Env, In] { type LOut = LOut0 }
}
@@ -9,22 +9,20 @@ import cats.syntax.monadError._
import shapeless.Poly1

import scala.concurrent.TimeoutException
import scala.concurrent.duration.FiniteDuration

object PromiseMapper extends Poly1 {
implicit def mkOne[F[_]: Timer, A](implicit F: Concurrent[F]) =
at[Protocol.Aux[A]] { protocol => (queueAndDuration: (Queue[F], FiniteDuration)) =>
queueAndDuration match {
case (queue: Queue[F], duration: FiniteDuration) =>
Deferred[F, Maybe[A]].flatMap { promise =>
queue.enqueue1(Request(protocol, promise.complete)) >> {
promise.get
.timeout(duration)
.adaptError {
case _: TimeoutException => RequestTimedOut(protocol)
}
private[this] final def mapper[F[_]: Concurrent: Timer, A](protocol: Protocol.Aux[A]): Env[F] => F[Maybe[A]] = {
case (queue, duration) =>
Deferred[F, Maybe[A]] >>= { promise =>
queue.enqueue1(Request(protocol, promise.complete)) >> {
promise.get
.timeout(duration)
.adaptError {
case _: TimeoutException => RequestTimedOut(protocol)
}
}
}
}
}
}

implicit def mkOne[F[_]: Concurrent: Timer, A]: Case.Aux[Protocol.Aux[A], Env[F] => F[Maybe[A]]] = at[Protocol.Aux[A]](mapper(_))
}

This file was deleted.

0 comments on commit 72b0503

Please sign in to comment.
You can’t perform that action at this time.