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

Freestyle Effects for @tagless #507

Merged
merged 9 commits into from
Jan 12, 2018
4 changes: 2 additions & 2 deletions docs/src/main/tut/docs/effects/async/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ The imports for the _frees-async_ AsyncContext:

```tut:silent
import freestyle.async._
import freestyle.async.implicits._
import freestyle.free.async.implicits._
```

Now if we want to create a freestyle program which uses the `findBooks` function and returns all the books sorted by the year they were written, we can use the `AsyncM` effect:
Expand Down Expand Up @@ -93,7 +93,7 @@ We can run these programs using:
- Cats' `IO` as target effect type:

```tut:book
import freestyle.free.asyncCatsEffect.implicits._
import freestyle.async.catsEffect.implicits._

import scala.concurrent.{Await, ExecutionContext}
import scala.concurrent.duration.Duration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@
* limitations under the License.
*/

package freestyle.free.asyncCatsEffect
package freestyle.async
package catsEffect

import freestyle.async._
import freestyle.free.async._
import cats.effect.Effect

trait AsyncCatsEffectImplicits {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,21 @@
* limitations under the License.
*/

package freestyle.free.asyncCatsEffect
package freestyle.async
package catsEffect

import cats.effect.IO
import freestyle.free._
import freestyle.free.implicits._
import freestyle.free.async._
import freestyle.free.async.implicits._
import freestyle.free.asyncCatsEffect.implicits._
import freestyle.async.catsEffect.implicits._
import org.scalatest.{AsyncWordSpec, Matchers}
import scala.concurrent.ExecutionContext

class AsyncFs2Tests extends AsyncWordSpec with Matchers {
class AsyncCatsEffectTests extends AsyncWordSpec with Matchers {

implicit override def executionContext = ExecutionContext.Implicits.global
implicit override def executionContext: ExecutionContext = ExecutionContext.Implicits.global

"Async Cats Effect Freestyle integration" should {
"support IO as the target runtime" in {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,23 @@
* limitations under the License.
*/

package freestyle.free.asyncGuava
package freestyle.async
package guava

import cats.~>
import com.google.common.util.concurrent._
import freestyle.free._
import freestyle.async.AsyncContext
import java.util.concurrent.{Executor => JavaExecutor}

import scala.concurrent.ExecutionContext

trait AsyncGuavaImplicits {

class ListenableFuture2AsyncM[F[_]](implicit AC: AsyncContext[F], E: ExecutionContext)
extends FSHandler[ListenableFuture, F] {
override def apply[A](listenableFuture: ListenableFuture[A]): F[A] =
extends (ListenableFuture ~> F) {
override def apply[A](fa: ListenableFuture[A]): F[A] =
AC.runAsync { cb =>
Futures.addCallback(
listenableFuture,
fa,
new FutureCallback[A] {
override def onSuccess(result: A): Unit = cb(Right(result))

Expand Down Expand Up @@ -64,4 +63,4 @@ trait AsyncGuavaImplicits {

}

object implicits extends AsyncGuavaImplicits
object implicits extends AsyncGuavaImplicits
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,19 @@
* limitations under the License.
*/

package freestyle.free.asyncGuava
package freestyle.async
package guava

import java.util.concurrent.{Callable, Executors}

import cats.~>
import com.google.common.util.concurrent.{ListenableFuture, ListeningExecutorService, MoreExecutors}
import org.scalatest._
import freestyle.async.implicits._
import freestyle.free.async.implicits._

import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future}

class AsyncGuavaTests extends WordSpec with Matchers {
class AsyncGuavaTests extends WordSpec with Matchers with Implicits {

import ExecutionContext.Implicits.global

Expand All @@ -47,6 +46,7 @@ class AsyncGuavaTests extends WordSpec with Matchers {
})

val handler: ListenableFuture ~> Future = implicits.listenableFuture2Async[Future]

val conv: ListenableFuture[Void] => ListenableFuture[Unit] =
implicits.listenableVoidToListenableUnit

Expand Down
30 changes: 28 additions & 2 deletions modules/async/async/shared/src/main/scala/async.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package freestyle
import scala.util._
import scala.concurrent._

object async {
package object async {

/** An asynchronous computation that might fail. **/
type Proc[A] = (Either[Throwable, A] => Unit) => Unit
Expand All @@ -29,6 +29,17 @@ object async {
def runAsync[A](fa: Proc[A]): M[A]
}

def future2AsyncM[F[_], A](
future: Future[A])(implicit AC: AsyncContext[F], E: ExecutionContext): F[A] =
AC.runAsync { cb =>
E.execute(new Runnable {
def run(): Unit = future.onComplete {
case Failure(e) => cb(Left(e))
case Success(r) => cb(Right(r))
}
})
}

trait Implicits {
implicit def futureAsyncContext(implicit ec: ExecutionContext) = new AsyncContext[Future] {
def runAsync[A](fa: Proc[A]): Future[A] = {
Expand All @@ -42,5 +53,20 @@ object async {
}
}
}
object implicits extends Implicits

trait Syntax {

implicit def futureOps[A](f: Future[A]): FutureOps[A] = new FutureOps(f)

final class FutureOps[A](f: Future[A]) {

@deprecated("Use unsafeTo instead.", "0.6.0")
def to[F[_]](implicit AC: AsyncContext[F], E: ExecutionContext): F[A] = future2AsyncM[F, A](f)

def unsafeTo[F[_]](implicit AC: AsyncContext[F], E: ExecutionContext): F[A] = future2AsyncM[F, A](f)

}

}

}
29 changes: 4 additions & 25 deletions modules/async/async/shared/src/main/scala/free/async.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package freestyle.free
import scala.concurrent._
import scala.util._
import freestyle.async._
import freestyle.async.implicits._

object async {

Expand All @@ -30,18 +29,11 @@ object async {

class Future2AsyncM[F[_]](implicit AC: AsyncContext[F], E: ExecutionContext)
extends FSHandler[Future, F] {
override def apply[A](future: Future[A]): F[A] =
AC.runAsync { cb =>
E.execute(new Runnable {
def run(): Unit = future.onComplete {
case Failure(e) => cb(Left(e))
case Success(r) => cb(Right(r))
}
})
}
override def apply[A](future: Future[A]): F[A] = future2AsyncM[F, A](future)

}

trait Implicits {
trait FreeImplicits extends Implicits with Syntax {

implicit def freeStyleAsyncMHandler[M[_]](
implicit MA: AsyncContext[M]
Expand All @@ -52,18 +44,5 @@ object async {
}
}

trait Syntax {

implicit def futureOps[A](f: Future[A]): FutureOps[A] = new FutureOps(f)

final class FutureOps[A](f: Future[A]) {

def to[F[_]](implicit AC: AsyncContext[F], E: ExecutionContext): F[A] =
new Future2AsyncM[F].apply(f)

}

}

object implicits extends Implicits with Syntax
object implicits extends FreeImplicits
}
5 changes: 2 additions & 3 deletions modules/async/async/shared/src/main/scala/tagless/async.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package freestyle.tagless
import scala.concurrent._
import scala.util._
import freestyle.async._
import freestyle.async.implicits._

object async {

Expand All @@ -28,13 +27,13 @@ object async {
def async[A](fa: Proc[A]): FS[A]
}

trait Implicits {
trait AsyncImplicits extends Implicits with Syntax {

implicit def taglessAsyncMHandler[M[_]](implicit MA: AsyncContext[M]): AsyncM.Handler[M] =
new AsyncM.Handler[M] {
def async[A](fa: Proc[A]): M[A] = MA.runAsync(fa)
}
}

object implicits extends Implicits
object implicits extends AsyncImplicits
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import freestyle.free._
import freestyle.free.implicits._
import freestyle.free.async._
import freestyle.free.async.implicits._
import freestyle.async.implicits._

import scala.concurrent.{ExecutionContext, Future}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@

package freestyle.free

import freestyle.free.async.implicits._
import freestyle.async._
import freestyle.async.implicits._
import freestyle.free.async.Future2AsyncM
import freestyle.free.async.implicits._
import org.scalatest._
import cats.effect.{Effect, IO}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import cats.instances.future._
import cats.syntax.flatMap._
import cats.syntax.functor._
import freestyle.async.AsyncContext
import freestyle.async.implicits._
import freestyle.tagless._
import freestyle.tagless.async._
import freestyle.tagless.async.implicits._
Expand Down
28 changes: 8 additions & 20 deletions modules/effects/shared/src/main/scala/free/effects/either.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,28 @@
package freestyle.free
package effects

import cats.{Eval, MonadError}
import scala.util.control.NonFatal

object either {

final class ErrorProvider[E] {

@free sealed trait EitherM {
def either[A](fa: Either[E, A]): FS[A]
def error[A](e: E): FS[A]
def catchNonFatal[A](a: Eval[A], f: Throwable => E): FS[A]
}
val taglessV: freestyle.tagless.effects.either.ErrorProvider[E] = freestyle.tagless.effects.either[E]

trait Implicits {
implicit def freeStyleEitherMHandler[M[_]](
implicit ME: MonadError[M, E]): EitherM.Handler[M] = new EitherM.Handler[M] {
def either[A](fa: Either[E, A]): M[A] = fa.fold(ME.raiseError[A], ME.pure[A])
def error[A](e: E): M[A] = ME.raiseError[A](e)
def catchNonFatal[A](a: Eval[A], f: Throwable => E): M[A] =
try ME.pure(a.value)
catch {
case NonFatal(e) => ME.raiseError(f(e))
}
}
type EitherM[F[_]] = taglessV.EitherM.StackSafe[F]

val EitherM = taglessV.EitherM.StackSafe

trait FreeImplicits extends taglessV.Implicits {

class EitherFreeSLift[F[_]: EitherM] extends FreeSLift[F, Either[E, ?]] {
def liftFSPar[A](fa: Either[E, A]): FreeS.Par[F, A] = EitherM[F].either(fa)
}

implicit def freeSLiftEither[F[_]: EitherM]: FreeSLift[F, Either[E, ?]] =
new EitherFreeSLift[F]

}

object implicits extends Implicits
object implicits extends FreeImplicits
}

def apply[E]: ErrorProvider[E] = new ErrorProvider
Expand Down
19 changes: 4 additions & 15 deletions modules/effects/shared/src/main/scala/free/effects/error.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,13 @@
package freestyle.free
package effects

import cats.{Eval, MonadError}

object error {

@free sealed trait ErrorM {
def either[A](fa: Either[Throwable, A]): FS[A]
def error[A](e: Throwable): FS[A]
def catchNonFatal[A](a: Eval[A]): FS[A]
}
type ErrorM[F[_]] = freestyle.tagless.effects.error.ErrorM.StackSafe[F]

trait Implicits {
val ErrorM = freestyle.tagless.effects.error.ErrorM.StackSafe

implicit def freeStyleErrorMHandler[M[_]](
implicit ME: MonadError[M, Throwable]): ErrorM.Handler[M] = new ErrorM.Handler[M] {
def either[A](fa: Either[Throwable, A]): M[A] = fa.fold(ME.raiseError[A], ME.pure[A])
def error[A](e: Throwable): M[A] = ME.raiseError[A](e)
def catchNonFatal[A](a: Eval[A]): M[A] = ME.catchNonFatal[A](a.value)
}
trait FreeImplicits extends freestyle.tagless.effects.error.Implicits {

class ErrorFreeSLift[F[_]: ErrorM] extends FreeSLift[F, Either[Throwable, ?]] {
def liftFSPar[A](fa: Either[Throwable, A]): FreeS.Par[F, A] = ErrorM[F].either(fa)
Expand All @@ -45,5 +34,5 @@ object error {

}

object implicits extends Implicits
object implicits extends FreeImplicits
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
package freestyle.free
package effects

object implicits extends option.Implicits with error.Implicits
object implicits extends option.FreeImplicits with error.FreeImplicits
Loading