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

Is it possible to be effectful with F[_]: Async instead of IO ? #3

Open
ronanM opened this issue Aug 25, 2023 · 2 comments
Open

Is it possible to be effectful with F[_]: Async instead of IO ? #3

ronanM opened this issue Aug 25, 2023 · 2 comments

Comments

@ronanM
Copy link

ronanM commented Aug 25, 2023

No description provided.

@ronanM ronanM changed the title Is it possible to be effectful with F[_] : Async instead of IO ? Is it possible to be effectful with F[_]: Async instead of IO ? Aug 25, 2023
@luksow
Copy link
Collaborator

luksow commented Aug 26, 2023

Hi, thanks for a very good question that should be probably addressed in README :)

So sadly, at this point - no. IO is bolted hard in the internals. I've tried going tagless final style at the beginning but I had various problems with type interference. Especially directives were awkward to compose when they were typed with F[_]. At some point my friend suggested that this could be possible if wrapped into a trait like

trait DSL[F[_]: Async] {
// everything goes here
}

to avoid that but then, it's quite cumbersome to structure it properly.

I'm of course open to more ideas and even better - contributions :)

@MateuszKubuszok
Copy link

MateuszKubuszok commented Aug 27, 2023

Considering that most of the things within the DSL would not be able to use F in a meaningful way (it would use Async or even more powerful type class so not much of a constraint anyway), one can use LiftIO[F] to take the output of DSL and compose it into some Program[F]. And withing the DSL one can also take the resulting F[A] and convert it to IO[A] with some F ~> IO.

Actually, now that I think about it, it can be done with something similar to Dispatcher, which would convert F to IO rather than Future.

Maybe something like:

trait IODispatcher[F[_]]:
  def runToIO[A](fa: F[A]): IO[A]
object IODispatcher:
  def apply[F[_]: Async]: Resource[F, IODispatcher[F]] = Dispatcher[F].map { dispatcher =>
    new IODispatcher[F] {
      def runToIO[A](fa: F[A]): IO[A] = IO.defer {
        val (future, cancel) = dispatcher.unsafeToFutureCancelable(fa)
        IO.fromFuture(IO.pure(future)).onCancel(IO(cancel()))
      }
    }
  }


IODispatcher[F].use { ioDispatcher =>
  // Create Route using DSL.
  // When you have:
  //   fa: F[A]
  // and want to use it in onComplete do:
  //   onComplete(ioDispatcher.runToIO(fa))
  def routes(ws: WebSocketBuilder2[IO]): Route = ...
    
  LiftIO[F].liftIO {
    // Creates and runs server as IO[Unit],
    // which LiftIO will turn to F[Unit]
    EmberServerBuilder
      .default[IO]
      .withHost(ipv4"0.0.0.0")
      .withPort(port"8080")
      .withHttpWebSocketApp(ws => routes(ws).toHttpRoutes.orNotFound)
      .build
      .use(_ => IO.never)
  }
}

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

No branches or pull requests

3 participants