This subproject defines cats-effect instances for scalaz datatypes. Particularly useful if you're trying to use a project like fs2 or http4s with older scalaz code which is still using Task
or (😱) scalaz.effect.IO
.
libraryDependencies += "com.codecommit" %% "shims-effect" % "<version>"
Unfortunately, is no scala.js build for shims-effect due to the fact that scalaz-effect and scalaz-concurrent are only published for the JVM.
With this dependency replacing your conventional "com.codecommit" %% "shims"
dependency, you can replace your import shims._
with the following:
import shims.effect._
This replaces your shims._
import, it does not augment it! This is very important. Do not do this:
import shims._ // no no no!
import shims.effect._ // no no no!
No no no no...
One or the other, not both.
The reason for this is implicit ambiguity. Effect
is a subtype of cats.Monad
. This means that, if we just naively define an Effect[Task]
in a scope which also has import shims._
, then the Monad[Task]
which we get through Effect
is ambiguous with the Monad
which is automatically materialized from the scalaz.Monad[Task]
instance. This is also why these instances are being provided by shims, instead of by a third-party project (as it was originally).
- Obviously, the core shims subproject
- cats-effect 2.0.0
- scalaz-concurrent 7.2.28
Effect[scalaz.concurrent.Task]
Parallel[scalaz.concurrent.Task]
Sync[scalaz.effect.IO]
Don't use scalaz.effect.IO
. Honestly. It's strictly worse than every other option out there. Search/replace your imports today.
Just as cats-effect provides lifted versions of its core typeclasses (Sync
, Async
, etc) for the cats monad transformers, so to does shims-effect provide such lifted instances for the scalaz monad transformers. Specifically:
OptionT
ContextShift
LiftIO
Sync
Async
Concurrent
Kleisli
ContextShift
LiftIO
Sync
Async
Concurrent
EitherT
ContextShift
LiftIO
Sync
Async
Concurrent
Effect
(forL = Throwable
)ConcurrentEffect
(forL = Throwable
)
StateT
ContextShift
LiftIO
Sync
Async
WriterT
(givenMonoid[W]
)ContextShift
LiftIO
Sync
Async
Concurrent
Effect
ConcurrentEffect
You'll notice that several StateT
-related instances are missing (specifically: Concurrent
, Effect
, and ConcurrentEffect
given Monoid[S]
). This is intentional. These instances are highly dubious and I never should have added them to cats-effect.
As a caveat, scalaz.StateT
is not stack safe under repeated bind
s. This is problematic because all Sync
s are assumed to be stack-safe. Be very careful with this instance. It is included mostly because it's quite useful, but do not assume you can run arbitrarily long computational chains in the way that you can with cats.StateT
.
scalaz.effect.LiftIO
- Requires a
cats.effect.LiftIO[F]
. Note that this conversion does not go in the other direction!
- Requires a
This is sort of a quick FAQ on why this subproject doesn't do more than it currently does:
- Why not
ConcurrentEffect[Task]
?- Because it's unsound.
Task
doesn't provide particularly robust or safe cancellation support, and it has absolutely no notion of guaranteed finalization. Therefore, it would not be able to lawfully implement the functions inConcurrent
. Yes, this does mean that there are certain things you cannot do withTask
, but they wouldn't be safe anyway. - As an aside, it is theoretically possible to do this, but it would require reimplementing a lot of the safe preemption mechanisms which are built into
cats.effect.IO
, and frankly I just don't feel like there's a particular need for this. Just don't useTask
if you can avoid it.
- Because it's unsound.
- Why not materialize
cats.effect.LiftIO
?- Because it's unsound (this is a common theme). There's really no way to convert a
cats.effect.IO
to ascalaz.effect.IO
without blocking a thread, due to theasync
constructor. We can go in the other direction only becausescalaz.effect.IO
is strictly less powerful thancats.effect.IO
.
- Because it's unsound (this is a common theme). There's really no way to convert a
- Why not do something with
scalaz.Nondeterminism
?- Mostly because it's kind of useless. It seems intuitive that we could bidirectionally coerce it to a
cats.Parallel
, but this doesn't work broadly becauseParallel
is a sound concept andNondeterminism
is not.Nondeterminism
exposes a veryApplicative
-like API with a name that isn't "applicative", which sounds a lot likeParallel
except for the fact that it forces you to use the same type constructor for which you're also (presumably) exposing aMonad
and such. This is at the very least unsound in principle, sinceApplicative
andMonad
must be consistent by the laws, and it is most definitely dangerous in practice. To make matters worse,Nondeterminism
wraps a ton of very unsafe race functions around this interface. These functions are broken on a fundamental level and should never be used for any reason.
- Mostly because it's kind of useless. It seems intuitive that we could bidirectionally coerce it to a
- Is there value in
ST
,MVar
, or the other goodies inscalaz.effect
orscalaz.concurrent
?- Nope. At least, not from the standpoint of shims.
MVar
blocks and should never be used under any circumstances (if you want something likeMVar
, usecats.effect.concurrent.Ref
).ST
is quite cool, but its practical utility seems limited given that we can just callSync[F].delay(...)
.RegionT
is also cool, but about 95% of its practical functionality is superseded in a more convenient package by the resource management in fs2.
- Nope. At least, not from the standpoint of shims.
- What about
scalaz.effect.MonadIO
?- Naively-encoded MTL-style typeclasses don't work particularly well in Scala due to the fact that the compiler doesn't enforce coherence. There are two currently-accepted solutions to this problem: either split materializations (as in cats-mtl) so users are forced to be explicit about the fact that they have a
Monad[F]
and aMonadIO[F]
, or encode an implicit disambiguation lattice at the type level (as in scato and scalaz 8). Both solutions work; neither are compatible withscalaz.effect.MonadIO
.
- Naively-encoded MTL-style typeclasses don't work particularly well in Scala due to the fact that the compiler doesn't enforce coherence. There are two currently-accepted solutions to this problem: either split materializations (as in cats-mtl) so users are forced to be explicit about the fact that they have a