Skip to content

Commit

Permalink
Freestyle Effects for @tagless (#507)
Browse files Browse the repository at this point in the history
* Provides @tagless final support for freestyle effects

* Releases freestyle 0.5.4

* Adds the async syntax to tagless (#508)

* Addresses code review comments

* Fixes async guava

* Deprecates .to to be replaced by .unsafeTo

* Fix up docs. We are releasing 0.6.0

* Async cleanup

* asyncGuava renamed to guava, asyncCatsEffect renamed to catsEffect
  • Loading branch information
juanpedromoreno authored Jan 12, 2018
1 parent 111f99f commit f4fba80
Show file tree
Hide file tree
Showing 32 changed files with 1,152 additions and 224 deletions.
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

0 comments on commit f4fba80

Please sign in to comment.