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

supporting doobie #100

Closed
dvic opened this issue May 16, 2017 · 5 comments
Closed

supporting doobie #100

dvic opened this issue May 16, 2017 · 5 comments

Comments

@dvic
Copy link
Contributor

dvic commented May 16, 2017

Would you be interested in having support for Doobie effects (i.e., ConnectionIO)? Or is this out of the scope of the project? I have been playing with this idea and currently have the below interpreter as a proof of concept (requiring as last effect on the task Task). It currently rewrites in terms of monix.eval.Task but this would of course be generalized. The interpreter also handles errors, calling the configured Strategy ConnectionIO programs of the Doobie transactor, which are by default:

  • before (set autoCommit false)
  • after (commit)
  • oops (rollback)
  • always (connection close)
type TaskStack = Fx.fx1[Task]
def runConnectionIO(t: Transactor[Task, HikariDataSource])(
    implicit m1: Member.Aux[ConnectionIO, R, Fx.fx1[Task]]): Eff[TaskStack, A] = {

  for {
    c <- send[Task, TaskStack, Connection](t.connect(t.kernel))
    res <- {

      val eInterpreted: Eff[TaskStack, A] = {
        interpret.translate(e)(new Translate[ConnectionIO, TaskStack] {
          override def apply[X](kv: ConnectionIO[X]): Eff[TaskStack, X] = {
            TaskCreation.fromTask(kv.foldMap(t.interpret).run(c))
          }
        })
      }

      val beforeAfter: Task[A] = for {
        _   <- t.strategy.before.foldMap(t.interpret).run(c)
        res <- eInterpreted.into[TaskStack].runSequential
        _   <- t.strategy.after.foldMap(t.interpret).run(c)
      } yield res

      val always = t.strategy.always.foldMap(t.interpret).run(c)
      val oops   = t.strategy.oops.foldMap(t.interpret).run(c)

      send[Task, TaskStack, A](
        beforeAfter
          .recoverWith({ case err => oops *> Task.raiseError[A](err) })
          .doOnFinish(_ => always)
          .doOnCancel(oops *> always)
      )
    }
  } yield res
}
@edmundnoble
Copy link
Contributor

edmundnoble commented May 16, 2017

I think an eff-doobie module sounds like a good idea. I have a few gripes with your implementation:

a) It's fixed to Task, instead of an arbitrary catchable, suspendable monad as in doobie-fs2 proper (which I'm calling Out[_])
b) There is no reason to have an output of Eff[fx1[Out], A], because all you need is an Out[A]. Logically as well this makes sense, because collapsing all of the binds is required for correct error handling behavior.
c) Is there no built-in way in doobie to do all of this error handling and recovery logic?

Feel free to open a PR though.

@dvic
Copy link
Contributor Author

dvic commented May 16, 2017

Thanks, in that case I think I'm going to give it a shot (after addressing #98).

a) It's fixed to Task, instead of an arbitrary catchable, suspendable monad as in doobie-fs2 proper (which I'm calling Out[_])

Yes, I'll make sure to use the same monad restrictions as in doobie-fs2.

b) There is no reason to have an output of Eff[fx1[Out], A], because all you need is an Out[A]. Logically as well this makes sense, because collapsing all of the binds is required for correct error handling behavior.

Ah I see, you mean it's better to output directly (in this case) a Task[A] (using .detach)?

c) Is there no built-in way in doobie to do all of this error handling and recovery logic?

Yes, the Transactor has a Strategy which contains all the configurable bits (before, after, oops, and always). You can then use Strategy.wrap to apply this logic to ConnectionIO program or Strategy.wrapK to apply this logic to a Kleisli[F, Connection, ? program. But I'm not quite sure how to obtain a raw ConnectionIO or Kleisli[F, Connection, ? program form a generic stack in Eff, but we can discuss it in more detail when I prepare an initial PR.

@edmundnoble
Copy link
Contributor

Sure. Just so you know, you cannot do the last thing you mentioned. The effects may not commute over ConnectionIO, you need ConnectionIO to be the only thing in the stack. As well I would need to see tests that the interpreter preserves ConnectionIO's transactions without splitting them into smaller transactions.

@dvic
Copy link
Contributor Author

dvic commented May 19, 2017

Started working on this, but won't be able to release it before the release of doobie 0.4.2 (0.4.1 does not contain the new Transactor design that is needed).

@etorreborre
Copy link
Member

This is merged into master now.

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

Successfully merging a pull request may close this issue.

3 participants