-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Client Definitions based on free algebras - Unary Services #16
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing work @juanpedromoreno, Left minor comments that I'd like addressing before merging this but none of them blocking. Great stuff 👍
val defaultOptions = CallOptions.DEFAULT | ||
|
||
for { | ||
hi <- greetingClientM.sayHello(messageRequest, defaultOptions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these operations are not interdependent this could be better expressed in applicative style.
(a |@| b |@| c).map( ... )
|
||
// http://www.grpc.io/docs/guides/concepts.html | ||
def runProgram[F[_]](implicit M: Monad[F]) = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a monad constrain here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's required. Not sure if we could skip it somehow
import io.grpc.stub.StreamObserver | ||
|
||
@module | ||
trait GreetingClientM { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
|
||
} | ||
|
||
object implicits { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think users may be confused with this. Every time you import something with the name implicits
users may go for implicits._
right away. Since this is a demo is not critical but I think we should try avoiding nesting further implicit scopes once we hit the implicits
one. For this case I would just have:
object implicits extends ServerImplicits with ClientImplicits
|
||
import scala.collection.JavaConverters._ | ||
|
||
class ClientCallsMHandler[F[_]](implicit C: Capture[F], AC: AsyncContext[F]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have traditionally been parametrizing handlers with M[_]
instead of F[_]
since in other classes aggregating algebras F[_]
we have used it to refer to the coproduct. I'm fine if we call this F but in that case we should do this across all modules. Also it would be nice if we all as a team come up with a naming convention doc that we enforce across the entire org to make sure we are consistent and not confusing users.
class ClientCallsMHandler[F[_]](implicit C: Capture[F], AC: AsyncContext[F]) | ||
extends ClientCallsM.Handler[F] { | ||
|
||
def asyncUnaryCall[I, O]( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually want to call this things unary
etc...? Perhaps just async
, sync
and stream
would be enough. thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, as we have to differentiate internally each service (and @free
doesn't support method overloading), these are the new names: async
, asyncStreamServer
, asyncStreamClient
, asyncStreamBidi
, sync
, syncC
, syncStreamServer
, syncStreamServerC
, asyncM
case SetCompressorRegistry(registry) => acc.compressorRegistry(registry) | ||
case SetIdleTimeout(value, unit) => acc.idleTimeout(value, unit) | ||
case SetMaxInboundMessageSize(max) => acc.maxInboundMessageSize(max) | ||
}).asInstanceOf[ManagedChannelBuilder[_]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any way we can get rid of this cast?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
@@ -29,8 +29,8 @@ trait Syntax { | |||
|
|||
final class ServerOps(server: FreeS[GrpcServer.Op, Unit]) { | |||
|
|||
def bootstrapM[M[_]](implicit MM: Monad[M], handler: GrpcServer.Op ~> M): M[Unit] = | |||
server.interpret[M] | |||
def bootstrapM[F[_]](implicit M: Monad[F], handler: GrpcServer.Op ~> F): F[Unit] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the naming conventions mentioned earlier. should this just be M[_]: Monad
instead of an implicit arg that is not in use in the method body?
This PR solves partially the issue #11 (only unary services).
It brings some free algebras on top of Grpc for RPC clients. Concretely, it solves:
It also updates the demo code to use these algebras, composing RPC services as freestyle modules: