Skip to content
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

Composable, effect-agnostic lambdas #51

Closed
wants to merge 10 commits into from

Conversation

armanbilge
Copy link
Member

@armanbilge armanbilge commented Nov 19, 2021

This was #45, before we started experimenting on that branch 😁

This re-works the original IOLambda concept, with two goals:

  1. Better composability
  2. Effect agnosticism

A lambda is re-imagined as:

type Lambda[F[_], Event, Result] = (Event, Context) => F[Option[Result]]

And IOLambda now defines an abstract:

def handler: Resource[IO, Lambda[IO, Event, Result]]

This handler encapsulates the previously separate Setup: all resource acquisition goes into building the Lambda once, and that is the "setup". That Lambda is then installed as the handler.

From here, we have two axes on which we can do composition:

  1. Middlewares/builders, e.g.:
Lambda[F, Event, Result] => Lambda[F, Event, Result]
HttpRoutes[F] => Lambda[F, ApiGatewayProxyEventV2, ApiGatewayProxyStructuredResultV2]
  1. Resource-injection, which is simply flatMapping resources:
def routes[F[_]: Async](client: Client[F], db: Session[F]): HttpRoutes[F] = ???

def handler = for {
 client <- EmberClientBuilder.default[IO].build
 db <- skunk.Session.single[IO](...)
} yield Http4sLambda(routes(client, db))

@bpholt and I have put this to good use in #45 and #50 to create a Natchez tracing middleware for lambda and I'm pretty confident that this is the right approach.

package feral

package object lambda {
type Lambda[F[_], Event, Result] = (Event, Context[F]) => F[Option[Result]]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any thoughts if this should just be an alias for Kleisli?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any advantage to making it so?

One very minor downside: I think making it Kleisli would mean, when running it, we'd generally have to wrap the event and context in two sets of parens, since it would expect a tuple instead of being a Function2.

Copy link
Member Author

@armanbilge armanbilge Nov 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kleisli is enriched with lots of nice methods that are also (more likely to be) stack-safe.
http://typelevel.org/cats/api/cats/data/Kleisli.html

since it would expect a tuple instead of being a Function2

Yep, exactly my reasoning for avoiding this.

Very similar discussion in http4s/http4s#4846.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the http4s experience and the fact that it sounds like they're going to move away from the Kleisli alias, I'm inclined to think we probably shouldn't introduce it here. But I don't have a super strong feeling either way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A third option is trait Lambda[F[_], Event, Result] that may or may not extend Function2 😅

Copy link
Member Author

@armanbilge armanbilge Nov 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A fourth option 😆 http4s will be rewriting its middlewares to use MTL-style Ask/Local instead of directly using Kleisli. Since we are taking a middleware-oriented approach here, perhaps we should do the same.
http4s/http4s#4758

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying this out in #60.

armanbilge and others added 2 commits November 19, 2021 17:15
Co-authored-by: Brian P. Holt <bholt+github@planetholt.com>
@armanbilge armanbilge mentioned this pull request Nov 22, 2021
Copy link
Member

@djspiewak djspiewak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple thoughts here…

What's the advantage to Lambda itself being parametric in F? I see the advantage with Context and I think that's a good change, but the parametricity doesn't seem all that helpful in Lambda's case.

Unrelatedly… The new Setup encoding seems really strange to me, unless I'm misunderstanding what it is meant to be doing. Is this orthogonal to the Resource support idea?

@armanbilge
Copy link
Member Author

armanbilge commented Nov 29, 2021

What's the advantage to Lambda itself being parametric in F?

Not really sure how to answer this. Why is http4s HttpRoutes/HttpApp parametric in F? I think these are pretty similar ideas.

The new Setup encoding seems really strange to me, unless I'm misunderstanding what it is meant to be doing. Is this orthogonal to the Resource support idea?

Hmm, after re-encoding the old way seemed really strange to me :P

Not sure what you mean by orthogonal. Instead of Setup being an (optional) type parameter, (what used to be) Setup is now actually just the lambda handler i.e. (Event, Context) => Result. We use the Resource to build that lambda once, and we install it for all requests. Does that make more sense?

@armanbilge armanbilge mentioned this pull request Dec 9, 2021
6 tasks
@armanbilge
Copy link
Member Author

Superseded by #60.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants