Code Monkey home page Code Monkey logo

cats-mtl's Introduction

Cats MTL

Provides transformer typeclasses for cats' Monads, Applicatives and Functors.

You can have multiple cats-mtl transformer typeclasses in scope at once without implicit ambiguity, unlike in pre-1.0.0 cats or Scalaz 7.

Usage

libraryDependencies += "org.typelevel" %% "cats-mtl" % "1.3.0"

If your project uses ScalaJS, replace the double-% with a triple. Note that cats-mtl has an upstream dependency on cats-core version 2.x.

Cross-builds are available for Scala 2.12, 2.13, 3, Scala Native, and ScalaJS major version 1.x.

If you're not sure where to start or what Cats MTL even is, please refer to the getting started guide.

Supported Classes

  • EitherT
  • Kleisli
  • IorT
  • OptionT
  • ReaderWriterStateT
  • StateT
  • WriterT

Laws

The cats-mtl-laws artifact provides Discipline-style laws for all of the type classes defined in cats-mtl. It is relatively easy to use these laws to test your own implementations of these typeclasses. Take a look here for more.

libraryDependencies += "org.typelevel" %% "cats-mtl-laws" % "1.3.0" % Test

These laws are compatible with both Specs2 and ScalaTest.

Documentation

Links:

  1. Website: typelevel.org/cats-mtl/
  2. ScalaDoc: typelevel.org/cats-mtl/api/

Related Cats links (the core):

  1. Website: typelevel.org/cats/
  2. ScalaDoc: typelevel.org/cats/api/

Community

People are expected to follow the Scala Code of Conduct when discussing cats-mtl on the Github page, Gitter channel, or other venues.

We hope that our community will be respectful, helpful, and kind. If you find yourself embroiled in a situation that becomes heated, or that fails to live up to our expectations, you should disengage and contact one of the project maintainers in private. We hope to avoid letting minor aggressions and misunderstandings escalate into larger problems.

License

All code is available to you under the MIT license, available at http://opensource.org/licenses/mit-license.php and also in the COPYING file.

cats-mtl's People

Contributors

andyscott avatar armanbilge avatar b-vennes avatar bpholt avatar catostrophe avatar danslapman avatar denisrosca avatar dhinojosa avatar djspiewak avatar edmundnoble avatar enzief avatar implmnt avatar kailuowang avatar kubukoz avatar larsrh avatar lukajcb avatar marcinzh avatar mpilquist avatar odersky avatar pchpsky avatar rossabaker avatar scala-steward avatar sh0hei avatar slamdata-bot avatar systemfw avatar typelevel-steward[bot] avatar valydia avatar xuwei-k avatar yuvalitzchakov avatar zelenya avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cats-mtl's Issues

Missing tests

The following test classes need to be written:

  • ApplicativeLocal[R => ?, R]
  • ApplicativeLocal[Reader[R, ?], R]
  • ApplicativeLocal[ReaderT[M, R, ?], R]
  • ApplicativeLocal for all transformers over ReaderT
  • MonadState[State[S, ?], S]
  • MonadState[StateT[M, S, ?], S]
  • MonadState for all transformers over StateT
  • FunctorListen for all transformers over WriterT
  • ApplicativeHandle[E Either ?, E] (deleted)
  • ApplicativeHandle[EitherT[M, E, ?], E] (deleted)
  • ApplicativeHandle for all transformers over EitherT (and nested combos of OptionT and EitherT layers) (deleted)
  • FunctorLayerFunctor for EitherT, WriterT, OptionT, ReaderT
  • ApplicativeLayerFunctor for EitherT, WriterT, OptionT, ReaderT
  • MonadLayerControl for EitherT, WriterT, OptionT, ReaderT, StateT
  • TFunctor for EitherT, WriterT, OptionT, StateT, ReaderT (deleted)

New typeclass to run reader-like effects

In short one may need to mimic creating and running ReaderT subprogram using Tagless Final approach.

Consider this endpoint in some app without TF:

class Endpoints(fooService: FooService, barService: BarService, ...){
  def endpoints = HttpRoutes.of[IO] {
    case ROOT -> "handle" => for {
        body <- req.as[Body]
        trace = genTrace
        logContext = buildCtx(body)
        auth = req.authInfo
        result <- handle.run(auth, trace, logContext)
        response <- Ok(result)
      } yield response
  }
  def handle(body: Body): ReaderT[IO, (Auth, Trace, LogCtx), Result] = ???
  //this method calls multiple other services and methods which also return `ReaderT[IO, ...]`. 
  //some of them are using context from reader to provide contextual logging or to avoid passing authorization as method parameters
}

And AFAIK there is no way to represent this behavior in tagless final code right now.

So here comes my proposal: cats-mtl should have some typeclass which can represent this, let's call it Run:

trait Run[F[_], G[_], R] {
  def run(fa: G[A])(r: R): F[A]
}

it should provide context of type R to computation ga of type G[A] to evaluate it to another computation of typeF[A].

So now we can write the endpoint like that:

class Endpoints[Main[_], Contextual[_]](fooService: FooService[Contextual], barService: BarService[Contextual], ...)(implicit run: Run[Main, Contextual, (Auth, Trace, LogCtx)]) {

  def endpoints = HttpRoutes.of[Main] {
    case ROOT -> "handle" => for {
        body <- req.as[Body]
        trace = genTrace
        logContext = buildCtx(body)
        auth = req.authInfo
        result <- run.run(handle(body))((auth, trace, logContext)) //  Main[Result]
        response <- Ok(result)
      } yield response
  }
  def handle(body: Body): Contextual[Result] = ???
  //this method calls multiple services and methods which look like FooService[Contextual]`. 
}

//and creation of endpoints:
type Context = (Auth, Trace, LogCtx)
val endpoints = Endpoint[IO, ReaderT[IO, Context, *], Context](foo, bar) // or could be zio.Task and zio.RIO[Context, *]

So what do you think about this proposal? I could try to implement this if it seems like a good idea.

Move the layer type classes into another package

For all users of this library that don't define their own transformers, they're likely to never see or use these type classes, so having them in the main package feels pretty unnecessary. @SystemFw suggested they go into their own lifting package.

Bracket for MTL

Hi all.

Bracket typeclass, that exists in cats-effect, doesn't work well with an effect that relies on FunctorRaise.
I want to implement a clean-up logic in case of any error (raised either by MonadError or FunctorRaise).

Example:

import cats.data.EitherT
import cats.effect._
import cats.effect.syntax.bracket._
import cats.mtl._
import cats.mtl.implicits._

final case class AppError(reason: String)

class Service[F[_]: Sync: ApplicativeHandle[?[_], AppError]: FunctorRaise[?[_], AppError]] {

  def createActive: F[String] =
    create.bracketCase(uuid => makeActive(uuid)) {
      case (_, ExitCase.Completed)   => Sync[F].unit // There can be an error too
      case (uuid, ExitCase.Error(e)) => destroy(uuid)
      case (uuid, ExitCase.Canceled) => destroy(uuid)
    }

  def makeActive(uuid: String): F[String] = FunctorRaise[F, AppError].raise(AppError("Oops. Something went wrong"))
  def create: F[String]                   = Sync[F].pure("some uuid")
  def destroy(uuid: String): F[Unit]      = Sync[F].unit

}

object Service {

  def main(args: Array[String]): Unit = {
    type Effect[A] = EitherT[IO, AppError, A]
    val service = new Service[Effect]

    println(service.createActive.value.unsafeRunSync()) // Left(AppError("Oops. Something went wrong"))
  }

}

For sure this is the correct behavior of a Bracket typeclass and clean-up logic for a specific error can be managed inside the bracket:

create.bracketCase(uuid => makeActive(uuid).handleError[AppError](e => destroy(uuid) >> e.raise)) { ... }

But should there be an alternative Bracket that manages specific errors raised by FunctorRaise? For example, extended ADT can be used:

sealed trait ExitCase[+E1, +E2]
object ExitCase {
  final case object Completed                  extends ExitCase[Nothing, Nothing]
  final case class Error[E](cause: E)          extends ExitCase[E, Nothing]
  final case class UnhandledError[E](cause: E) extends ExitCase[Nothing, E]
  final case object Canceled                   extends ExitCase[Nothing, Nothing]
}

Add @implicitNotFound to all classes

The contents should guide users towards what to do. For example, for the Ask classes, it should say something like this:

If you have a value of $A in scope, or a way of getting one, then you should wrap $F in Kleisli (e.g. Kleisli[$F, $A, ...) and use that type with this function. If you do not have a value of type $A in scope, then you should add an implicit F: ApplicativeAsk[$F, $A] parameter to your function.

We should be able to do this for all of the classes.

0.1.0 release

Some pre-requisites for a 0.1.0 release are:

  • The release of cats 1.0.0-MF
  • Adding missing tests, laws, and syntax (#6, #7, #8)
  • Adding basic docs (#2, #3, #4)
  • TraverseEmpty, for cats parity (#5)

It would be nice to have a formulation for MonadBracket (#9) but it doesn't seem likely to appear in time.

I'm projecting that this will take a week and a half.

Add low-priority materializations for Handle in terms of ApplicativeError

Given an ApplicativeError[F, E], we should be able to get a Handle[F, E] (which implies Raise[F, E]). This would be very useful in that it allows us to get something like Raise[IO, Throwable], which extends the generality of the library somewhat. It shouldn't cause any ambiguity issues if it is pushed to the lowest priority of the resolution chain in the Handle companion.

Add a MonadChronicle type class

MonadChronicle is the mtl equivalent of IorT and has some fairly nice properties if you ask me. We should consider adding support for it :)

FunctorListen doesn't understand nested Monad stack

I'm trying to stack a WriterT and EitherT together - ultimately something like IO[Writer[Log, Either[Throwable, A]]]. The idea is that even in the event of an error the log up to that point should be maintained.

I have created a POC which uses a transformer stack of EitherT[WriterT[IO, Log, *], Throwable, *] and when I materialise the program it works as expected. The logs are present even in the event of an error. However FunctorListen seems to short circuit in the event of an error. Here is the code itself:

object POC extends IOApp {

  import cats.instances.vector._
  import cats.mtl.implicits._
  import cats.syntax.flatMap._
  import cats.syntax.functor._

  type Log = Vector[String]

  override def run(args: List[String]): IO[ExitCode] = {
    val name = getName[EitherT[WriterT[IO, Log, *], Throwable, *]]
    // this works even with a raised error so the stack itself is working
    name.value.written.flatTap(logs => IO(println(logs))).as(ExitCode.Success)
    // this won't work
    printLog(name).value.run.as(ExitCode.Success)
  }

  def printLog[F[_], A](fa: F[A])(implicit F: Sync[F], FL: FunctorListen[F, Log]): F[A] = {
    // this is never called when an error is raised
    fa.listen.flatMap { case (a, logs) => F.delay(println(logs)).as(a) }
  }

  def getName[F[_]](implicit F: Sync[F], FT: FunctorTell[F, Log], FR: FunctorRaise[F, Throwable]): F[String] = for {
    _ <- FT.tell(Vector("getting name ..."))
    a <- F.delay("bob")
    // Exception here means FunctorListen cant find any logs
    _ <- FR.raise(new Exception("boom!")): F[Unit]
  } yield a

}

I'm not sure if this is a problem with FunctorListen or FunctorRaise. It could be that FunctorRaise is raising an error at the IO level not the Either/EitherT

Inconsistent use of def vs val in embedded typeclass

In Ask:

def applicative: Applicative[F]

In Censor:

val applicative: Applicative[F]

So if you define traits that extend both, like:

trait MyTrait[F[_], Env, Evt]  extends Censor[F, Chain[Evt]] with Ask[F, Env]
trait MyTrait2[F[_], Env, Evt] extends Ask[F, Env] with Censor[F, Chain[Evt]]

The order becomes important. MyTrait2 compiles but MyTrait fails with:

stable, immutable value required to override:
[error] val applicative: cats.Applicative[F] (defined in trait Censor)
[error]   with def applicative: cats.Applicative[F] (defined in trait Ask)
[error]   trait MyTrait[F[_], Env, Evt] extends Censor[F, Chain[Evt]] with Ask[F, Env]

How about using the same pattern to avoid such issues?

Add a cats-mtl-free submodule

This would serve two purposes. First, inductive instances over FreeT. Second, we could define some nifty machinery to implement certain typeclasses via InjectK algebrae within Free/FreeT, which would be awesome.

Change Gitter room of Travis build notifications

I noticed that even thought there is a cats-mtl Gitter channel, the Travis build notifications are going to the Cats Gitter channel instead. I don't have write permissions to this repo, so I can't see the right URL to fix it, but someone with permissions should be able to go to the cats-mtl Gitter channel, click the settings knobs on the upper right, click Integrations and set up a new webhook URL for Travis.

If you leave the URL hard-coded in the .travis.yml then you will still get notifications for builds on forks. Also I don't think that there's anything preventing anyone from copying the URL and sending build notifications for unrelated projects (though I don't know what their motive would be). See typelevel/cats#2691 for how to easily transition to the preferred way of setting up these notifications.

Ensure all classes implicitly narrow/widen as appropriate

For example, the Ask classes should implicitly widen, while the Error classes should implicitly narrow. The former is equivalent to making Kleisli contravariant, while the latter is equivalent to making EitherT covariant, but critically implicit encodings of this work even on polymorphic types. Quick example:

object ApplicativeAsk {
  implicit def widen[F[_], A, B >: A](implicit F: ApplicativeAsk[F, A]): ApplicativeAsk[F, B] = ...

README.md

Have a nice explanatory README.md, which gives users the gist of the library. I also plan to fold DESIGN.md and CONTRIBUTING.md into this file, for easy discovery.

Missing syntax classes

The following is a list of type classes with unimplemented or mostly unimplemented syntax files:

  • ApplicativeHandle (no file) (deleted)
  • FunctorTell (missing utility methods)
  • FunctorListen (missing new utility methods)
  • MonadLift (no file) (deleted)
  • FunctorEmpty
  • TraverseEmpty

Port PrivateInstances from meow-mtl?

I always find it a bit surprising that e.g. evidence of MonadState does not provide evidence of Monad.

So if I want to write

def foo[F[_]](...)(implicit F: MonadState[F, MyState]) =
  for {
    a <- firstThing()
    b <- secondThing(a)
  } yield b

then I have to add a : Monad context bound. But that feels redundant - it's a MonadState, so of course it's a Monad!

If you're using meow-mtl, this can be fixed by importing com.olegpy.meow.prelude._, thanks to PrivateInstances.

Is there a good reason not to make that functionality available out of the box in cats-mtl?

They are using shapeless LowPriority, which implies there might be cases where you would want your Monad[F] evidence to be different from the val monad: Monad[F] member of MonadState, but I can't think of a use case where I would want that. Maybe I'm missing something.

CONTRIBUTING.md

Detail:

  • the way to add a new type class
  • the way to add an instance
  • the way to add a passthrough instance

Explore a submodule for more performant instances.

As many have said before, stacking multiple monad transformers can be quite inefficient, it'd be cool if we had some kind of story for supporting performant instances for different combination of mtl classes.
Oleg has already done some work in that regard with meow-mtl and at the start of the year, I've also played around with specialized instances at cats-mtl-special.

Revisit naming conventions

The names here (e.g. FunctorTell) are all super arbitrary. There's very little prior art here, since most other MTL systems use encodings that avoid orphans (e.g. Haskell and Scalaz 8), so the cats-mtl calculus is pretty unique. What we should ask ourselves is whether or not the names, as they are currently chosen, appropriately guide users to the meaning and use-cases.

Versioning scheme?

We should commit a versioning scheme pre-1.0.

I think going with no changes in patch versions and breakages in minor versions seems ideal for now. We should also document this somewhere :)

Missing laws

The following is a list of type classes with missing laws classes:

  • FunctorLayer
  • FunctorLayerFunctor
  • ApplicativeLayer
  • ApplicativeLayerFunctor
  • MonadLayer
  • MonadLayerFunctor
  • MonadLayerControl
  • MonadLift (deleted)
  • MonadState
  • MonadHandle (deleted)
  • TFunctor (deleted)
  • TraverseEmpty

The ones that do not have documented laws are:

  • MonadLayerControl
  • MonadLift (deleted)
  • TFunctor (deleted)
  • TraverseEmpty

TFunctor not implementable by StateT without functor constraint

trait TFunctor[T[_[_], _]] {
  def mapT[F[_], G[_], A](tfa: T[F, A]): T[G, A]
}

This unfortunately is not implementable for StateT (F[S => F[(S, A)]]), because you need to be able to map over one of the F[_] levels to apply the natural transformation at the other level. Two solutions come to mind:
a) Require either the source F[_] or the target G[_] to have a Functor instance, allowing the user to map over either if required (only for StateT, afaik), by providing mapTS[F[_]: Functor, G[_], A](tfa: T[F, A]): T[G, A] as well as mapTT[F[_], G[_]: Functor, A](tfa: T[F, A]): T[G, A] to satisfy the maximum number of usecases. This is implemented currently.
b) Drop the TFunctor instance for StateT.

Reconsider the `MonadState => ApplicativeAsk` and `FunctorTell` implication.

Right now we have two instances that give you an ApplicativeAsk[F, E] if there's a MonadState[F, E] instance in scope, and a FunctorTell[F, L] instance when MonadState[F, L] is in scope.

These instances are probably useful and lawful, but it still doesn't quite feel right for me.
I kind of expect the environment parameter E in ApplicativeAsk to be immutable and never change, whereas if I have a StateT[F, E], it can change with each flatMap.
I'm not sure if this is a problem or not, but wanted to see what others think about this. :)

Currently not-covered code

There is some code which I am aware has no tests, most of which I'm not sure yet how to deal with.
As of July 26 2017, these account for every single coverage miss.

  • Default operations of FunctorRaise
  • Instances of FunctorRaise

I'm not sure if FunctorRaise is necessary.

  • zero from MonadLayerControl

Not sure if necessary, if it is it'll be for some kind of bracketing.

  • Some Laws and Tests smart constructors which are not called because the calling code is always overriden

Really not sure what to do about this one. I could use early initialisers but that's voodoo.

  • The backwards part of the local and scope isomorphisms

There are no MonadLayers that are not MonadLayerFunctors at the moment. Thus this generality is not currently used; ContT would change this.

  • Lifting ApplicativeAsk and FunctorTell through layers

Could be implemented with local implicit scoping.

MonadBracket

Need a way to think about bracketing in terms of monad stacks, to avoid any layers short-circuiting or using state incorrectly.

Hand-write (or scalafix) syntax stuff

A lot of the classes could use some nice syntax (e.g. a raise[F] function enriched onto error types). Since we aren't using Simulacrum, this needs to be done… not with Simulacrum. It can be done by hand, or preferably with scalafix.

DESIGN.md

Explain why cats-mtl is designed the way it is, with justification.

  • Type class encoding
  • Layer/LayerFunctor/LayerControl hierarchy
  • MonadLift (removed)
  • FunctorFunctor, ApplicativeFunctor, MonadFunctor, etc hierarchies (removed)

Add revised MonadLog?

If we adapt the MonadLog type class presented in IO & Logging Capabilities to fit as an extension to FunctorListen, we end up with the following. We've replaced log with tell and extract is replaced by listen.

trait MonadLog[F[_], L] extends FunctorListen[F, L] {
  val monad: Monad[F]

  val monoid: Monoid[L]

  def clear[A](fa: F[A]): F[A]

  def flush[A](fa: F[A])(f: L => F[Unit]): F[A]
}

Personally, I don't think it's the right way to go:

  • we're extending from a Functor hierarchy, while we require Monad,
  • this makes us require not one, but two additional constraints, with Monoid,
  • logs are forced to take a form like F[(A, L)], and cannot be stored separately.

Instead, I propose we adapt MonadLog as an extension to MonadState. In fact, any MonadState[F, L] can be a MonadLog[F, L] with a Monoid[L] instance, meaning we can reuse the existing MonadState instances. Such an adapted version of MonadLog could be defined as follows.

trait MonadLog[F[_], L] extends MonadState[F, L] with Serializable {
  val monoid: Monoid[L]

  def log(l: L): F[Unit]

  def clear: F[Unit]

  def flush(f: L => F[Unit]): F[Unit]
}

Default implementation could be defined as follows.

trait DefaultMonadLog[F[_], L] extends DefaultMonadState[F, L] with MonadLog[F, L] {
  def log(l: L): F[Unit] =
    modify(monoid.combine(_, l))

  def clear: F[Unit] =
    set(monoid.empty)

  def flush(f: L => F[Unit]): F[Unit] =
    monad.flatMap(monad.flatMap(get)(f))(_ => clear)
}

Laws could be defined as follows.

trait MonadLogLaws[F[_], L] extends MonadStateLaws[F, L] {
  implicit val logInstance: MonadLog[F, L]
  implicit val monoid: Monoid[L] = logInstance.monoid

  import logInstance._
  import monad.{pure, unit}
  import monoid.empty

  def logThenClearDoesNothing(l: L) =
    log(l) *> clear <-> unit

  def logThenGetReturnsLog(l: L) =
    log(l) *> get.flatMap(clear.as) <-> log(l) *> clear.as(l)

  def logsThenGetReturnsCombinedLog(l1: L, l2: L) =
    log(l1) *> log(l2) *> get.flatMap(clear.as) <->
      log(l1) *> log(l2) *> clear.as(l1 combine l2)

  def clearThenGetReturnsEmpty =
    clear *> get <-> pure(empty)

  def clearThenClearClearsOnce =
    clear *> clear <-> clear

  def flushIsGetThenClear(f: L => F[Unit]) =
    flush(f) <-> get.flatMap(f) *> clear
}

Rewrite away the LayerControl stuff

MonadLayerControl (and similar) are a clever mechanism for reducing the implementation boilerplate of MTL-like libraries (such as this one), but they also prevent such libraries from interacting with non-algebraic monad transformers, such as FreeT, ContT, and many more.

In order to avoid these issues, as well as make the library generally more maintainable and avoid some of the issues with implicit resolution, we should rewrite all of that away and use a more conventional lifting mechanism for each class. For trivial constructor algebras (such as MonadState), we can still do auto-lifting using a mechanism like scalaz's MonadPartialOrder, but non-algebraic effects (like handleErrorWith or local) need to be lifted more explicitly on a bespoke basis. This is, in fact, the greatest strength of the MTL approach: the fact that lifting those effects is tailored to the stack in question, rather than generalized in a way which causes unsoundness in certain permutations (such as the way in which Eff works).

Consider facility to view State as Reader

It can be convenient to allow a MonadState[F, S] to serve as an ApplicativeAsk[F, S]. We can think of the former constraint as the ability to read and update system state, and the latter as simply the ability to read it. If all we need is to read the state, the least privilege principle suggests that a ApplicativeAsk[F, S] constraint is preferable.

Traditionally, Reader monad is associated with reading outside configuration. However, IME it also works well to read the domain model of a stateful application, or some part thereof eg by the lensing techniques available in meow-mtl.

An example of value can be found in the meow-mtl readme use case

This is a proposal to consider an opt-in extra derivation that provides an ApplicativeAsk[F, S] should a MonadState[F, S] instance be available. "Opt-in" rather than automatic because the Ask- and State- may be unrelated, if S-type is something common like Int.

Simplify the crazy build configuration to something less crazy

You know, I consider myself a reasonably advanced user of TravisCI and sbt, and it took me a zillion tries just to get a PR to build successfully. I still have no idea how to release anything. Some of those tries involved running sbt locally, having something fail, killing it, running the same command again and having it work.

I want to rip out a lot of the madness so that it's possible for third-parties to contribute without having to replicate what I did, because I'm not sure I can replicate what I did.

Invert several higher-order type signatures

There are a number of type signatures which are backwards for no apparent reason other than the fact that Haskell did it that way. For example:

def local[A](f: E => E)(fa: F[A]): F[A]

This is exactly backwards from Scala's standpoint and forces a) poor call-site syntax, and b) extremely poor type inference in many cases. The two parameters should be reversed.

Can't migrate from v0.7: StateT[EitherT, ...], ...] vs. EitherT[StateT, ...], ...]

Cats-MTL v0.7.1 allows both arrangements.

Cats-MTL v1.1.1 case works only when EitherT is on the top.

Using: cats-mtl v0.7.1

https://scastie.scala-lang.org/vIEWTZkrTlGyel2jBiwRoA

import cats.{Monad, Id}
import cats.data.{StateT, EitherT}
import cats.implicits._
import cats.mtl.{MonadState, FunctorRaise}
import cats.mtl.implicits._

object Main extends App {
  def computation[F[_]: Monad: MonadState[*[_], Int]: FunctorRaise[*[_], String]](a: Int): F[Int] = a.pure[F]
  type MyEither[A] = EitherT[Id, String, A]
  type MyState[A] = StateT[MyEither, Int, A]
  val result = computation[MyState](42).run(0).value  // <====== NO ERROR
  println(result)
}

Using: cats-mtl v1.1.1

https://scastie.scala-lang.org/bZE05pflSnKOjpPiGmksjg

import cats.{Monad, Id}
import cats.data.{StateT, EitherT}
import cats.implicits._
import cats.mtl.{Stateful, Raise}
import cats.mtl.implicits._

object Main extends App {
  def computation[F[_]: Monad: Stateful[*[_], Int]: Raise[*[_], String]](a: Int): F[Int] = a.pure[F]
  type MyEither[A] = EitherT[Id, String, A]
  type MyState[A] = StateT[MyEither, Int, A]
  val result = computation[MyState](42).run(0).value  //  <======  ERROR
  println(result)
}

Compiler output

Could not find an implicit instance of Raise[Main.MyState, String]. If you have
a good way of handling errors of type String at this location, you may want
to construct a value of type EitherT for this call-site, rather than Main.MyState.
An example type:

  EitherT[Main.MyState, String, *]

This is analogous to writing try/catch around this call. The EitherT will
"catch" the errors of type String.

If you do not wish to handle errors of type String at this location, you should
add an implicit parameter of this type to your function. For example:

  (implicit fraise: Raise[Main.MyState, String])

Manually defining the missing Raise instance using def raise(e) = StateT.liftF(EitherT.leftT(e)) makes it work.

TraverseEmpty

The counterpart to TraverseFilter in cats has to be filled in.

  • Tests
  • Laws
  • Class
  • Syntax

(note: must compose over Traverse)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.