Existential types and monad transformers - haskell

Context: I'm trying to produce an error monad that also keeps track of a list of warnings, something like this:
data Dangerous a = forall e w. (Error e, Show e, Show w) =>
Dangerous (ErrorT e (State [w]) a)
i.e. Dangerous a is an operation resulting in (Either e a, [w]) where e is a showable error and w is showable.
The problem is, I can't seem to actually run the thing, mostly because I don't understand existential types all that well. Observe:
runDangerous :: forall a e w. (Error e, Show e, Show w) =>
Dangerous a -> (Either e a, [w])
runDangerous (Dangerous f) = runState (runErrorT f) []
This doesn't compile, because:
Could not deduce (w1 ~ w)
from the context (Error e, Show e, Show w)
...
`w1' is a rigidtype variable bound by
a pattern with constructor
Dangerous :: forall a e w.
(Error e, Show e, Show w) =>
ErrorT e (State [w]) a -> Dangerous a
...
`w' is a rigid type variable bound by
the type signature for
runDangerous :: (Error e, Show e, Show w) =>
Dangerous a -> (Either e a, [w])
I'm lost. What's w1? Why can't we deduce that it's ~ w?

An existential is probably not what you want here; there is no way to "observe" the actual types bound to e or w in a Dangerous a value, so you're completely limited to the operations given to you by Error and Show.
In other words, the only thing you know about w is that you can turn it into a String, so it might as well just be a String (ignoring precedence to simplify things), and the only thing you know about e is that you can turn it into a String, you can turn Strings into it, and you have a distinguished value of it (noMsg). There is no way to assert or check that these types are the same as any other, so once you put them into a Dangerous, there's no way to recover any special structure those types may have.
What the error message is saying is that, essentially, your type for runDangerous claims that you can turn a Dangerous into an (Either e a, [w]) for any e and w that have the relevant instances. This clearly isn't true: you can only turn a Dangerous into that type for one choice of e and w: the one it was created with. The w1 is just because your Dangerous type is defined with a type variable w, and so is runDangerous, so GHC renames one of them to avoid name clashes.
The type you need to give runDangerous looks like this:
runDangerous
:: (forall e w. (Error e, Show e, Show w) => (Either e a, [w]) -> r)
-> Dangerous a -> r
which, given a function which will accept a value of type (Either e a, [w]) for any choices of e and w so long as they have the instances given, and a Dangerous a, produces that function's result. This is quite hard to get your head around!
The implementation is as simple as
runDangerous f (Dangerous m) = f $ runState (runErrorT m) []
which is a trivial change to your version. If this works for you, great; but I doubt that an existential is the right way to achieve whatever you're trying to do.
Note that you'll need {-# LANGUAGE RankNTypes #-} to express the type of runDangerous. Alternatively, you can define another existential for your result type:
data DangerousResult a = forall e w. (Error e, Show e, Show w) =>
DangerousResult (Either e a, [w])
runDangerous :: Dangerous a -> DangerousResult a
runDangerous (Dangerous m) = DangerousResult $ runState (runErrorT m) []
and extract the result with case, but you'll have to be careful, or GHC will start complaining that you've let e or w escape — which is the equivalent of trying to pass an insufficiently polymorphic function to the other form of runDangerous; i.e. one that requires more constraints on what e and w are beyond what the type of runDangerous guarantees.

Ok, I think I figured out what I was floundering after:
data Failure = forall e. (Error e, Show e) => Failure e
data Warning = forall w. (Show w) => Warning w
class (Monad m) => Errorable m where
warn :: (Show w) => w -> m ()
throw :: (Error e, Show e) => e -> m ()
instance Errorable Dangerous where
warn w = Dangerous (Right (), [Warning w])
throw e = Dangerous (Left $ Failure e, [])
(instance Monad Dangerous and data DangerousT help too.)
This allows you to have the following code:
foo :: Dangerous Int
foo = do
when (badThings) (warn $ BadThings with some context)
when (worseThings) (throw $ BarError with other context)
data FooWarning = BadThings FilePath Int String
instance Show FooWarning where
...
and then in your main module you may define custom instances of Show Failure, Error Failure, and Show Warning and have a centralized way to format your error messages, for example
instance Show Warning where show (Warning s) = "WARNING: " ++ show s
instance Show Failure where ...
let (result, warnings) = runDangerous function
in ...
Which, in my opinion, is a pretty cool way to handle errors and warnings. I've got a working module that's something like this, now I'm off to polish it up and maybe put it on hackage. Suggestions appreciated.

Related

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 understand the abstration of RST, Lensed and LensT in Snaplet?

I am reading the source of Snap recently, which is great, but when I moved on to read the Snaplet Handler source, I got stuck at the abstraction of RST,Lensed and LensT.
newtype RST r s m a = RST { runRST :: r -> s -> m (a, s) }
newtype LensT b v s m a = LensT (RST (Lens b v) s m a)
newtype Handler b v a = Handler (LensT (Snaplet b) (Snaplet v) (Snaplet b) Snap a)
Now LensT changed to Lensed
newtype Lensed b v m a = Lensed { unlensed :: ALens' b v -> v -> b -> m (a, v, b) }
And the Snaplet Design said We switched to a slightly more specialized monad formulation called Lensed that avoids traversal of the whole state hierarchy when the state is manipulated.
I feel like there is a gap between the implementation of Snap and Snaplet Handler, and the key is RST, LensT and Lensed, is there any reference documentation to help me out?
TL;DR - There's no gap. The Handler definition you pasted is out of date. The current one uses Lensed.
Long answer: We don't have any documentation on this because it is a low-level implementation detail--i.e. all of it is completely hidden from the end user. Carl is right that RST is just RWST minus the W, but let's do some deeper investigation. Using the types that you show above, we'll substitute the definition of RST into the LensT definition. This gives us the following substitutions:
r = Lens b v
s = s
m = m
a = a
With that we can easily write the expanded LensT definition:
newtype LensT b v s m a = LensT { unlensT :: Lens b v -> s -> m (a, s) }
Compare that to Lensed:
newtype Lensed b v m a = Lensed { unlensed :: ALens' b v -> v -> b -> m (a, v, b) }
If we assume that Lens b v and ALens' b v are interchangeable (which, conceptually speaking, they are), then you can see this equivalence:
Lensed b v m a = LensT b v (b,v) m a
Now we see the crux of the issue. LensT is a more general construct than Lensed. LensT allows you to choose your s arbitrarily. Lensed fixes s to be completely determined by b and v. Now that we understand the difference, the question is how are these two constructs actually used in Snap. A quick grep through Types.hs shows us that Handler uses Lensed and Initializer uses LensT. (A side note: the definition you give for Handler is not the one we're currently using.) Here are the important parts of the definitions.
Handler b v a = Handler (L.Lensed (Snaplet b) (Snaplet v) ...)
Initializer b v a = Initializer (LT.LensT (Snaplet b) (Snaplet v) (InitializerState b)...)
Initializer uses the more general LensT construction because it needs s to be InitializerState, which contains extra information not related to b and v. In fact, the whole point of Initializer's existence is to facilitate the construction of a b that will be used as Handler's initial state. Initializer runs when your application starts up, but Handler is what your application runs in. We wanted Handler to be as efficient as possible, so we created Lensed to be optimized for exactly the needs of Handler. You could argue that was premature optimization, but since someone else did it for me, I wasn't going to say no.
You might wonder why we even have Lensed and LensT at all. They're only used in one place so we could just substitute their definitions into Handler and Initializer respectively. That's because we didn't have Lensed at the very beginning. Both Handler and Initializer were written in terms of LensT, and therefore it was a perfectly reasonable abstraction to eliminate duplicated code. Lensed came later and since they are all newtypes anyway, those layers of abstraction impose zero runtime cost.

Type Constraints in Typeclass

For fun I'm building a parser library. In this library I have a Parser data type:
data Parser e a = Parser (String -> Either e (a, String))
I'm able to define Functor and Applicative instances of Parser, but I don't think I can make an Alternative instance without constraining the "error" type or the "value" type the parser could return. Originally, this made me create an Applicative instance for when the error types are String messages, but I realized that I should be able to release this constraint to any message data type that has an Alternative (or maybe Monoid instead?) instance as well. With that in mind I wrote this:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
instance Alternative e => Alternative (Parser e)
where
empty = Parser $ \s -> Left empty
(<|>) (Parser p1) (Parser p2) = Parser $ \s -> tryParser s p2 $ p1 s
where
tryParser s p2 (Left _ ) = p2 s
tryParser _ _ x = x
Unfortunately, this fails to compile. When I load it into ghci I get this error message:
Parsertest.hs:31:47:
Expecting one more argument to `e'
In the instance declaration for `Alternative (Parser e)'
Failed, modules loaded: none.
When I search online, this seems to be the solution, but it doesn't work for me. What am I missing?
The problem is that Alternative is for type constructors of kind * -> *, so if you say
instance Alternative e => ....
then e has to be a type constructor, not a type. So e could be [] or Maybe or something, but not Int.
Alternative's operator is <|> which has type Alternative e => e a -> e a -> e a. This forces e to take an argument to make a type, like Maybe has to take an argument, eg Maybe Int.
Use Monoid instead of Alternative, because it's a type class instead of a constructor class. It's operator mappend has type Monoid e => e -> e -> e which is what you want for combining errors.

Parameterising Partial Functions By Error Type

I have a situation in which the majority of the operations I am using are partial functions. I want the return type of the functions to vary depending on the context, which should determine the kind of exception handling that occurs, and the type of errors that are reported. My current solution is to define a function parameterised by an error type, then implement the operation as an open function using type classes. For example, the following is an implementation of head, assuming a suitable implementation of Error and MonadError for Maybe in terms of the unit type:
class (Error e, MonadError e m) => Head m e | m -> e where
head :: [a] -> m a
errorHead :: (Error e, MonadError e m) => e -> [a] -> m a
errorHead e [] = throwError e
errorHead e (x : xs) = return x
instance Head Maybe () where
head = errorHead ()
instance Head (Either String) String where
head = errorHead "error: empty list"
The advantage of this implementation is that different errors can be thrown depending on the context in which head is called. The question I have is whether this can be accomplished without defining the function using an auxiliary operation and open functions. If not, the second question is whether this solution is optimal. In particular, is there a better way to achieve this behavior?
The http://hackage.haskell.org/package/errors package has many utilities for moving between Maybe, Either, MaybeT, and EitherT.

Ambiguous type variables for dependent class constraints

I'm writing a new authentication system for the Snap web framework, because the built-in one isn't modular enough, and it has some features that are redundant/"dead weight" for my application. This problem isn't related to Snap at all, though.
While doing so, I hit a problem with ambiguous type constraints. In the following code, it seems obvious to me that the type of back can only be the type variable b in the functions type, yet GHC complains that the type is ambiguous.
How can I change the following code such that the type of back is b, without using e.g. ScopedTypeVariables (because the problem is with the constraint, not with having too general types)? Is there a functional dependency that is needed somewhere?
Relevant type classes:
data AuthSnaplet b u =
AuthSnaplet
{ _backend :: b
, _activeUser :: Maybe u
}
-- data-lens-template:Data.Lens.Template.makeLens
-- data-lens:Data.Lens.Common.Lens
-- generates: backend :: Lens (AuthSnaplet b u) b
makeLens ''AuthSnaplet
-- Some encrypted password
newtype Password =
Password
{ passwordData :: ByteString
}
-- data-default:Data.Default.Default
class Default u => AuthUser u where
userLogin :: Lens u Text
userPassword :: Lens u Password
class AuthUser u => AuthBackend b u where
save :: MonadIO m => b -> u -> m u
lookupByLogin :: MonadIO m => b -> Text -> m (Maybe u)
destroy :: MonadIO m => b -> u -> m ()
-- snap:Snap.Snaplet.Snaplet
class AuthBackend b u => HasAuth s b u where
authSnaplet :: Lens s (Snaplet (AuthSnaplet b u))
The code that fails:
-- snap:Snap.Snaplet.with :: Lens v (Snaplet v') -> m b v' a -> m b v a
-- data-lens-fd:Data.Lens.access :: MonadState a m => Lens a b -> m b
loginUser :: HasAuth s b u
=> Text -> Text -> Handler a s (Either AuthFailure u)
loginUser uname passwd = with authSnaplet $ do
back <- access backend
maybeUser <- lookupByLogin back uname -- !!! type of back is ambiguous !!!
-- ... For simplicity's sake, let's say the function ends like this:
return . Right . fromJust $ maybeUser
Full error:
src/Snap/Snaplet/Authentication.hs:105:31:
Ambiguous type variables `b0', `u0' in the constraint:
(HasAuth s b0 u0) arising from a use of `authSnaplet'
Probable fix: add a type signature that fixes these type variable(s)
In the first argument of `with', namely `authSnaplet'
In the expression: with authSnaplet
In the expression:
with authSnaplet
$ do { back <- access backend;
maybeUser <- lookupByLogin back uname;
... }
src/Snap/Snaplet/Authentication.hs:107:16:
Ambiguous type variable `b0' in the constraint:
(AuthBackend b0 u) arising from a use of `lookupByLogin'
Probable fix: add a type signature that fixes these type variable(s)
In a stmt of a 'do' expression:
maybeUser <- lookupByLogin back uname
In the second argument of `($)', namely
`do { back <- access backend;
maybeUser <- lookupByLogin back uname;
... }'
In the expression:
with authSnaplet
$ do { back <- access backend;
maybeUser <- lookupByLogin back uname;
... }
I would venture to guess that the root of your problem is in the expression with authSnaplet. Here's why:
∀x. x ⊢ :t with authSnaplet
with authSnaplet
:: AuthUser u => m b (AuthSnaplet b1 u) a -> m b v a
Don't mind the context, I filled in some bogus instances just to load stuff in GHCi. Note the type variables here--lots of ambiguity, and at least two that I expect you intend to be the same type. The easiest way to handle this is probably to create a small, auxiliary function with a type signature that narrows things down a bit, e.g.:
withAuthSnaplet :: (AuthUser u)
=> Handler a (AuthSnaplet b u) (Either AuthFailure u)
-> Handler a s (Either AuthFailure u)
withAuthSnaplet = with authSnaplet
Again, pardon the nonsense, I don't actually have Snap installed at the moment, which makes things awkward. Introducing this function, and using it in place of with authSnaplet in loginUser, allows the code to type check for me. You may need to tweak things a bit to handle your actual instance constraints.
Edit: If the above technique doesn't let you nail down b by some means, and assuming that the types really are intended to be as generic as they're written, then b is impossibly ambiguous and there's no way around it.
Using with authSnaplet eliminates b entirely from the actual type, but leaves it polymorphic with a class constraint on it. This is the same ambiguity that an expression like show . read has, with instance-dependent behavior but no way to pick one.
To avoid this, you have roughly three choices:
Retain the ambiguous type explicitly, so that b is found somewhere in the actual type of loginUser, not just the context. This may be undesirable for other reasons in the context of your application.
Remove the polymorphism, by only applying with authSnaplet to suitably monomorphic values. If the types are known in advance, there's no room for ambiguity. This potentially means giving up some polymorphism in your handlers, but by breaking things apart you can limit the monomorphism to only code that cares what b is.
Make the class constraints themselves unambiguous. If the three type parameters to HasAuth are, in practice, interdependent to some degree such that there will only be one valid instance for any s and u, then a functional dependency from the others to b would be completely appropriate.

Resources