has-transformers 0.1.0.3 → 0.1.0.4
raw patch · 4 files changed
+29/−20 lines, 4 filesdep ~hspecPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: hspec
API changes (from Hackage documentation)
Files
- CHANGELOG.md +4/−0
- README.md +21/−16
- has-transformers.cabal +2/−2
- src/Control/Monad/Trans/Has.hs +2/−2
CHANGELOG.md view
@@ -11,3 +11,7 @@ ## 0.1.0.2 -- 2021-12-05 * Improved readme++## 0.1.0.4 -- 2022-01-26++* Support GHC 9.0
README.md view
@@ -52,7 +52,7 @@ and be able to change your monad stack without breaking existing code? Then simply use the [`Has`](https://hackage.haskell.org/package/has-transformers/docs/Control-Monad-Trans-Has.html#t:Has) typeclass,-and replace all your `lift` orgies with its one function, `liftH`.+and replace all your `lift` orgies with its one function, [`liftH`](https://hackage.haskell.org/package/has-transformers/docs/Control-Monad-Trans-Has.html#v:liftH). But isn't this a solved problem, you ask? Read on.@@ -78,7 +78,7 @@ And if you want to add your custom transformer `FooT`, not only do you have to implement `MonadReader` etc. for all the classes you need, you also have to invent your own `MonadFoo`,-and implement instanes for all transformers for it.+and implement instances for all transformers for it. This is well-known, and called the "quadratic instance problem". If you or someone else has written a good blog post or article about this topic, let me know, I'll link to it.@@ -167,7 +167,7 @@ | [`has-transformers`](https://www.github.com/turion/has-transformers/) | ✅ | ❌ | ✅ | ✅ | | [`mtl`](https://hackage.haskell.org/package/mtl) | ❌ | ✅ | ✅ | ✅ | | [`fused-effects`](https://hackage.haskell.org/package/fused-effects) | ✅ | ✅ | ✅ | ✅ |-| [`polysemy`](https://hackage.haskell.org/package/polysemy), [`freer-simple`](https://hackage.haskell.org/package/freer-simple), ... | ✅ | ❌ | ✅ | ✅ |+| [`polysemy`](https://hackage.haskell.org/package/polysemy), [`freer-simple`](https://hackage.haskell.org/package/freer-simple), ... | ✅ | ✅ | ❌ | ✅ | | [`rio`](https://hackage.haskell.org/package/rio) | ✅ | ❌ | ✅ | ❌ | Let me know if other features & libraries are important to you.@@ -186,16 +186,16 @@ and `catchError` to handle any such exception. Such a stack satisfies `Has (ExceptT e) m`,-and you can use `liftH` to define [`throw :: HasExcept e m => e -> m ()`](https://www.github.com/turion/has-transformers/src/Control/Monad/Trans/Has/Except.hs),+and you can use [`liftH`](https://hackage.haskell.org/package/has-transformers/docs/Control-Monad-Trans-Has.html#v:liftH) to define [`throw :: HasExcept e m => e -> m ()`](https://www.github.com/turion/has-transformers/src/Control/Monad/Trans/Has/Except.hs), but nothing like `catchError`. The reason is that the type signature of `catchError` is _higher order_ in the monad `m`: It appears on the left-hand side of the final `->`. In other words, `catchError` does not only return something of type `m a`, it also _expects inputs_ related to the type `m`.-But if you look at the type signature of `liftH`,+But if you look at the type signature of [`liftH`](https://hackage.haskell.org/package/has-transformers/docs/Control-Monad-Trans-Has.html#v:liftH), you will notice that `m` only appears on the right hand side of the `->`,-so it is not possible to implement a function like `catchError` with `liftH`.+so it is not possible to implement a function like `catchError` with [`liftH`](https://hackage.haskell.org/package/has-transformers/docs/Control-Monad-Trans-Has.html#v:liftH). This excludes many advanced operations on effects that you might be interested in, e.g. continuation passing, error handling, logic backtracking, and others.@@ -206,12 +206,16 @@ ### So what's your recommendation on effect systems? My opinionated, limited-point-of-view recommendation,-without knowing your use case,-is that you should use `transformers` and give `has-transformers` a try if you have a small project that doesn't do anything too fancyful with effects,-and when you hit stuff like complex error handling, backtracking, and so on,-upgrade to `fused-effects` if you don't get a headache with the types,-or migrate to `polysemy` (or maybe `freer-simple`) otherwise.+without knowing your use case, is: +* You have a small project that doesn't do anything too fancyful with effects:+ Use `transformers` and give `has-transformers` a try.+ Don't bother with `mtl` and other effect libraries.+* When you hit stuff like complex error handling, backtracking, and so on:+ Upgrade from `has-transformers` to `fused-effects`.+* You don't get a headache from `fused-effects`:+ Migrate to `polysemy` (or maybe `freer-simple`).+ ## How? The central insight is that a transformer `t` is not only an effect handler,@@ -226,10 +230,6 @@ and that `t` is one of these transformers, i.e. `t` is in the monad stack `m`. -### How do I interpret the effects?--...- ### How do I separate effect signatures and handlers? First of all, note that you don't always have to, and that can be a simplification:@@ -264,7 +264,12 @@ or [`msplit :: MonadLogic m => m a -> m (Maybe (a, m a))`](https://hackage.haskell.org/package/logict/docs/Control-Monad-Logic-Class.html#t:MonadLogic), which each define the minimal interface for their respective `mtl`-style class. They need `m` on the left hand side of the final arrow,-and thus cannot be encoded in the form of `liftH`.+and thus cannot be encoded in the form of [`liftH`](https://hackage.haskell.org/package/has-transformers/docs/Control-Monad-Trans-Has.html#v:liftH).++You can still have `ContT` in your transformer stack though!+The only issue is that you cannot address the effects introduced by it.+When using `has-transformers`, adding `ContT` to your stack doesn't break any code.+But when using `ContT`, `has-transformers` will not give you any benefit in those situations where you actually need to use any functions from `ContT`. ## (Why) `Has`n't this been done before?
has-transformers.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: has-transformers-version: 0.1.0.3+version: 0.1.0.4 synopsis: This library 'Has' transformers @@ -62,6 +62,6 @@ build-depends: base >= 4.14 && < 5 , has-transformers- , hspec >= 2.9.0+ , hspec >= 2.8.0 , operational build-tool-depends: hspec-discover:hspec-discover
src/Control/Monad/Trans/Has.hs view
@@ -23,10 +23,10 @@ -- | If the transformer is outermost, -- the action can be inserted as-is. instance Monad m => Has t (t m) where- liftH = id+ liftH action = action {-# INLINE liftH #-} --- | If the target transformer @t@ is under a different layer @t1@, 'lift' once.+-- | If the target transformer @t@ is under a different layer @t1@, 'lift' once and recurse. instance {-# Overlappable #-} (Monad m, MonadTrans t1, Has t m) => Has t (t1 m) where liftH action = lift $ liftH action {-# INLINE liftH #-}