Defining a MonadEither type class - haskell

I'm going back through Monad Transformers : Step by Step as a refresher, and like many tutorials out there, it uses Control.Monad.Error. GHC now gives a warning that this module is deprecated, so I switched over to Control.Monad.Trans.Either from the either library: http://hackage.haskell.org/package/either-3.4/docs/Control-Monad-Trans-Either.html
Everything is handled smoothly with eval2 in the paper, since EitherT is the outermost monad. However, after that everything falls apart -- ReaderT is in no way an Either value, and everything henceforth uses ErrorT, which I'd like to change to EitherT.
My idea, then, was to define a MonadEither type class that boxed left and right in order to handle errors, but this hasn't been fruitful. I don't really understand how the type classes in mtl work, and this instance in particular has to be parameterized over multiple values, which is confusing. I came up with the following, which compiles after including some syntactic extensions:
class (Monad m) => MonadEither l r m | m -> r where
right :: r -> m r
left :: l -> m r
But I can't figure out a MonadEither instance of EitherT:
instance Monad m => MonadEither l r (E.EitherT l m) where
right = E.right
left = E.left
Edit: I changed the instance declaration to match E.EitherT properly, and get the following error message:
Illegal instance declaration for ‘MonadEither l r (E.EitherT l m)’
The coverage condition fails in class ‘MonadEither’
for functional dependency: ‘m -> r’
Reason: lhs type ‘E.EitherT l m’ does not determine rhs type ‘r’
In the instance declaration for ‘MonadEither l r (E.EitherT l m)’
Again, I'm not really sure what I'm doing. I don't really understand functional dependencies, so I'm just looking for some guidance as to what an appropriate MonadEither type class might look like, if possible to define.

How about
instance Monad m => MonadEither l r (E.EitherT l m)
That is, it should be l instead of r.
However once you've done this you'll come up across a separate error. The root cause is that there's no point to right; it's just return. This means you need to get rid of the r parameter to the class.
class Monad m => MonadEither l m where
left :: l -> m a
Your instance declaration should then become
instance Monad m => MonadEither l (E.EitherT l m)
(You also may want to look at the MonadError class since this is essentially what you are replicating.)

Related

Haskell class definition question about restrictions

class IndexSelect k (m :: k -> (* -> *) -> *) | m -> k where
type Restriction m (p :: k) :: Constraint
indexSelect :: Restriction m p => Sing (p :: k) -> Proxy m -> LocalDb f -> f (TableEntity (m p))
I'm new to the Haskell language. I'm having trouble deciphering the class definition in some code in the code repo at the company I just started at.
What is this doing?
There is a lot going on here. I am going to start by referring you to Sections 7.6 Class and Instance Declarations, 7.7 Type Families, and 7.8.4 Explicitly-kinded qualifications of the GHC language extension documentation. (I am by no means an expert on any of these and clicked on your question hoping someone had supplied further enlightenment.)
We are defining a multi-parameter type class called IndexSelect with parameters k and m. (Multi-parameter type classes 7.6.1.1)
The second parameter of the class, m, is given an explicit kind qualification: k -> (* -> *) -> * in English m must be a function which takes a k and a function and returns a value. (7.8.4 Explicitly-kinded quantification)
The class has a functional dependency | m -> k. Where the choice of m must uniquely determine k Given the name of this function that implies that a collection m must have only one kind of key k which is reasonable. (7.6.2 Functional Dependencies)
The class forms a indexed type family type Restriction m (p :: k) :: Constraint. It appears inside a class definition so it is an associated type synonym. (7.7.2.1.1 Associated type family declarations). It takes some m and a p which must be of type k and results in a Constraint.
The class has one listed method indexSelect which one might guess manages to extract information from a collection. Without knowing what Sing, LocalDb and TableEntity do I cannot say more.

Satisfy the condition that 'A uniquely determines B' when using Functional Dependencies in Haskell

I am trying to write a ‘logger’ Monad Transformer. Other Monad Transformers will then be applied to it, to form a more complex monad. I want the logger function to work on all these monads, so I wrote a typeclass as follows.
class Logger e m | m -> e where
logMessage :: e -> m ()
The reason I use Functional Dependencies here, is that the monad m will explicitly contain the type e (as what it is with State monad), which stands for the message type.
The transformer ET is made an instance of typeclass Logger.
data ET e m a = ET { ... }
instance Monad m => Monad (ET e m) where
logMessage msg = ...
instance Monad m => Logger e (ET e m) where
logMessage msg = ...
Now, I want the monad T1 (T2 ... (ET m)) (which has an ET in the transformer chain) to be an instance of typeclass Logger, but it failed to compile. Below is the code.
instance (Logger e m, MonadTrans t) => Logger e (t m) where
logMessage = lift . logMessage
I thought that since t is only a Monad Transformer, and m is guaranteed to uniquely determine e, then t m should also uniquely determine e. But the compiler seems to think differently.
Test.hs:43:10: error:
? Illegal instance declaration for ‘Logger e (t m)’
The coverage condition fails in class ‘Logger’
for functional dependency: ‘m -> e’
Reason: lhs type ‘t m’ does not determine rhs type ‘e’
Un-determined variable: e
Using UndecidableInstances might help
? In the instance declaration for ‘MonadException e (t m)’
|
43 | instance (Logger e m, MonadTrans t) => Logger e (t m) where
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
Can anyone explain how the extension FunctionalDependencies works, as well as how to solve this problem?
The compiler I use is The Glorious Glasgow Haskell Compilation System, version 8.2.2, on Windows 10.
The trouble is that while m -> e it doesn't follow that t m -> e because the compiler doesn't know anything about what t might do.
What you have defined is not actually a monad transformer, its a class of Logger monads. Canonically, the mtl way of approaching this would be:
Define a class (Monad m) => MonadLogger e m | m -> e (just rename your existing class).
Define newtype LoggerT e m a = LoggerT(runLoggerT :: m <...>). This is your monad transformer. runLoggerT unwraps a LoggerT action and returns a value in the inner monad m. The details of what it returns are up to you.
Create instances of Monad, MonadTrans and MonadLogger for LoggerT.
Define type Logger e = Logger e Identity
Create inductive instances for all the other mtl monad transformers.
If you take a look at the examples in the mtl library you should be able to see how its done.
Many thanks to #Carl, this problem is solved.
When I turned on the language extension Undecidable Instances (by {-# LANGUAGE UndecidableInstances #-}), this error message disappeared.
Though still I wonder why this extension is needed, for now, it really makes the code compile.

How to abstract over monads without fighting the type system in Haskell?

I'm currently building a server in haskell and as a newbie to the language, I'd like to try a new approach zu Monad composition. The idea is that we can write library methods like
isGetRequest :: (SupportsRequests m r) => m Bool
isGetRequest = do
method <- liftRequests $ requestMethod
return $ method == GET
class (Monad m, RequestSupport r) => SupportsRequests m r | m -> r where
liftRequests :: r a -> m a
class (Monad r) => RequestSupport r where
requestMethod :: r Method
which work without knowing the underlying monad. Of course in this example, it would have been sufficient to make isGetRequest operate directly on the (RequestSupport r) monad but the idea is that my library might also have more than one constraint on the monad. Yet, I do not want to implement all of those different concerns in the same module nor spread them across different modules (orphan instances!).
That's why the m monad only implements the Supports* classes, delegating the real concerns to other monads.
The above code should work perfectly (with some language extensions to GHC). Unfortunately, I got some problems with the CRUD (Create Read Update Delete) concern:
class (Monad m, CRUDSupport c a) => SupportsCRUD m c a | m a -> c where
liftCRUD :: c x -> m x
class (Monad c) => CRUDSupport c a | c -> a where
list :: c [a] -- List all entities of type a
No I get an error:
Could not deduce (SupportsCRUD m c a0) from the context [...]
The type variable 'a0' is ambiguous [...]
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the class method: liftCRUD [...]
Seems like the type checker doesn't like that the a parameter does not directly arise in the signature of liftCRUD. That's understandable because a cannot be derived from the functional dependencies.
The type checker in my brain tells me that it should not be a problem to infer the type a later on, using AllowAmbiguousTypes, when some method regarding CRUD is executed in a library method. Unfortunately, GHC seems unable to do this inference step, for example
bookAvailable :: (SupportsCRUD m c Book) => m Bool
bookAvailable = do
books <- liftCRUD (list :: c [Book]) -- I use ScopedTypeVariables
case books of
[] -> return False
_ -> return True
yields
Could not deduce (SupportsCRUD m c0 a1) arising from a use of 'liftCRUD' [...]
The type variables c0, a1 are ambiguous [...]
It seems that I am still unable to reason about the compiler. I there a way to resolve this problem? Or at least a way to understand what the compiler is able to infer?
Best Regards,
bloxx
To use ScopedTypeVariables you also need to bind the variables you want to be in scope with forall. So it should be
bookAvailable :: forall m c. (SupportsCRUD m c Book) => m Bool
...
That was all that was necessary (after some trivial fixes I made which I assume were typos from entering your question) for me to get the code to compile.

Is there a typeclass for references similar to the MArray class for mutable arrays?

The MArray class provides generic functions for working with mutable arrays of various sorts in both ST and IO contexts. I haven't been able to find a similar class for working with both STRefs and IORefs. Does such a thing exist?
The ref-fd package provides it:
class Monad m => MonadRef r m | m -> r where
[...]
or with type families, ref-tf:
class Monad m => MonadRef m where
type Ref m :: * -> *
[...]
Another answer suggested the monad-statevar package which doesn't have the functional dependency. It also has separate HasGet and HasPut members and no abstraction over the newRef functionality.
Aside from the different methods in each, the functional dependency is a design trade-off. Consider the following two simplified classes:
class MRef1 r m where
newRef1 :: a -> m (r a)
readRef1 :: r a -> m a
class MRef2 r m | m -> r where
newRef2 :: a -> m (r a)
readRef2 :: r a -> m a
With MRef1, both the monad type and the reference type can vary freely, so the following code has a type error:
useMRef1 :: ST s Int
useMRef1 = do
r <- newRef1 5
readRef1 r
No instance for (MRef1 r0 (ST s)) arising from a use of `newRef1'
The type variable `r0' is ambiguous
We have to add an extra type signature somewhere to say that we want to use STRef.
In contrast, the same code works fine for MRef2 without any extra signature. The signature on the definition saying that the whole code has type ST s Int, combined with the functional dependency m -> r means that there is only one r type for a given m type and so the compiler knows that our existing instance is the only possible one and we must want to use STRef.
On the flip side, suppose we want to make a new kind of reference, e.g. STRefHistory that tracks all the values that have ever been stored in it:
newtype STRefHistory s a = STRefHistory (STRef s [a])
The MRef1 instance is fine because we are allowed multiple reference types for the same monad type:
instance MRef1 (STRefHistory s) (ST s) where
newRef1 a = STRefHistory <$> newSTRef [a]
readRef1 (STRefHistory r) = head <$> readSTRef r
but the equivalent MRef2 instance fails with:
Functional dependencies conflict between instance declarations:
instance MRef2 (STRef s) (ST s) -- Defined at mref.hs:28:10
instance MRef2 (STRefHistory s) (ST s) -- Defined at mref.hs:43:10
I also mentioned the type family version, which is quite similar in expressive power to the functional dependency; the reference type is a "type function" of the monad type so there can again only be one per monad. The syntax ends up being a bit different and in particular you can just say MonadRef m in constraints, without stating what the reference type is within the constraint.
It's also plausible to have the reversed functional dependency:
class MRef2 r m | r -> m where
so that each reference type can live in just one monad, but you can still have several reference types for a monad. Then you'd need type signatures on your references but not on your monadic computations as a whole.
Control.Monad.StateVar has a typeclass which lets you get and put IORefs and STRefs identically.

MonadTransControl instance for ProxyFast/ProxyCorrect

Using pipes, I'm trying to write an instance of MonadTransControl for the ProxyFast or ProxyCorrect type. This is what I've got:
instance MonadTransControl (ProxyFast a' a b' b) where
data StT (ProxyFast a' a b' b) a = StProxy { unStProxy :: ProxyFast a' a b' b Identity a}
liftWith = undefined
restoreT = undefined
I have no idea how to write liftWith or restoreT. The instances for the other monad transformers all use a function that "swaps" the monads, for example EitherT e m a -> m (EitherT e Identity a), but I couldn't find any such function in pipes. How does the instance for MonadTransControl for ProxyCorrect / ProxyFast look like? Or is it impossible to write one? (If yes, is it possible in pipes 4.0?)
Thanks for the link, and now I can give a better answer.
No, there is no way to implement this, using either version of pipes. The reason why is that MonadTransControl expects a monad transformer to be built on top of a single layer of the underlying base monad. This is true for all the monad transformers that MonadTransControl currently implements, such as:
ErrorT ~ m (Either e r)
StateT ~ s -> m (r, s)
WriterT ~ m (r, w)
ReaderT ~ i -> m r
ListT ~ m [r] -- This version of ListT is wrong, and the true ListT
-- would not work for `MonadTransControl`
However, a Proxy does not wrap a single layer of the base monad. This is true for both pipes versions where you can nest as many layers of the base monad as you want.
In fact, any monad transformer that nests the base monad multiple times will defy a MonadTransControl instance, such as:
FreeT -- from the `free` package
ListT -- when done "right"
ConduitM -- from the `conduit` package
However, just because pipes does not implement MonadTransControl doesn't mean that all hope is lost. pipes-safe implements many of the operations that one would typically expect from MonadTransControl, such as bracketing resource acquisitions, so if you can elaborate on your specific use case I can tell you more if there is an appropriate pipes-based solution for your problem.

Resources