# avail `avail` is a companion to monad transformers that allows you to impose effect constraints on *concrete monads*. Specifically, instead of writing ```haskell myApp :: (MonadWriter Log m, MonadState Store m, MonadReader Env m) => m () ``` it allows you to write ```haskell myApp :: Effs '[MonadWriter Log, MonadState Store, MonadReader Env] => App () ``` where `App` is a specific, concrete monad stack. ## Introduction Current effects libraries all have one principle of effect restriction: an effect can be used in a monad if it can be interpreted in terms of the monad. This works well with a polymorphic monad type, but a polymorphic type is unfriendly to compiler optimization. In contrast, a concrete monad can be easily optimized, but if we fix a monad that supplies all the effects we need, we can no longer restrict what effects each function can use. `avail` solves this problem with the [*phantom constraint pattern*](https://xn--i2r.xn--rhqv96g/2021/09/14/redundant-constraints/). We use a newtype wrapper `M` to screen out the user from directly manipulating the underlying monad, and performing any operation in a typeclass (for example, `MonadIO`) requires the corresponding *phantom* `Eff` constraint (in this case. `Eff MonadIO`) to be in scope. In other words, one can perform operations of a class only if: - The class is implemented for the monad, and - The *effect constraint* `Eff e` is available in the context. The second requirement decouples the availability of effects from the monad implementation. At last, we use a function `runM` to clear the constraints and restore the underlying monad. A typical example looks like this: ```haskell import Avail import Control.Monad.Reader import Control.Monad.State type App = M (ReaderT Int (State Bool)) testParity :: Effs '[MonadReader Int, MonadState Bool] => App () testParity = do num <- ask put (even num) example :: IO () example = do print $ runM @'[MonadReader Int, MonadState Bool] testParity & (`runReaderT` 2) & (`execState` False) print $ runM @'[MonadReader Int, MonadState Bool] testParity & (`runReaderT` 3) & (`execState` False) ``` Through microbenchmarks and tests in some applications, the performance of using `avail` is at least on par with, and often better than, using the polymorphic counterparts. ## Making `avail` work with new typeclasses `avail` already comes with support of all `mtl`, `exceptions`, `unliftio`, `monad-control` and [`capability`](https://hackage.haskell.org/package/capability) typeclasses. To add support for your own typeclass, for example: ```haskell class MonadOvO m where ... ``` You can use the Template Haskell utilities in the `Avail.Derive` module. ```haskell import Avail.Derive avail [t| MonadOvO |] ``` There may be other more complicated cases, such as dependencies between classes and multi-param classes: ```haskell class (MonadOvO m, MonadQwQ m) => MonadUwU r m where ... ``` `avail` gets you covered: ```haskell import Avail.Derive with1 \r -> avail' [ [t| MonadOvO |] , [t| MonadQwQ |] ] [t| MonadUwU $r |] ``` ## Limitations - Running effects: Because effect constraints in `avail` are detached from the monad structure, they cannot be run on a one-by-one basis. Practically, one can only run all effects and obtain the underlying concrete monad at once via `runM`. This means there is no exact equivalent to `runReaderT`, `runExceptT` etc on the `M` monad. If your application can entirely run on a single transformer stack (in particular, `ReaderT IO`, but also other transformer stacks), this is a non-issue because there will be no need to run effects one-by-one. For some other scenarios, there are some solutions that may be used solve this issue: - `local` is an almost identical substitute to `runReaderT` without destructing the monad. - Similarly, `tryError` is a substitute to `runExceptT`. - To simulate `runStateT`, simply set the value before the action and get the value after it. - `listen` is a very close analog to `runWriterT`. ## Where is `availability`? The old `availability` is abandoned due to its attempt at addressing two problems at once (capability *and* availability management), and may introduce confusion to new users on the choice between `capability` and `availability`. The new `avail` library focuses on availability management and acts not as a standalone effects library, but an alternative effect management layer that is used together with `mtl` or `capability` (which also reduces preformance overhead). The old `availability` can be found at [`re-xyr/availability-old`](https://github.com/re-xyr/availability-old).