Non type-variable argument for MonadWriter - haskell

I just tried to use writer:
writer (5, "Hello, World")
But I noticed I get an error:
Non type-variable argument in the constraint MonadWriter [Char] m
But I wonder what constraint I don't fulfill? [a] after all is a Monoid...

It’s just a check to keep type checking “simple”
writer (5, "Hello world") :: (MonadWriter String m, Num n) => m n
which is a perfectly sensible type. However, by default, it is not allowed, because the constraint MonadWriter String m contains a type argument String, which isn’t a variable. This was done with the intent that it would stop the delaying of type errors:
class C c where c :: c -> c
-- no instance C Int
x :: C Int => Int
x = c (5 :: Int)
-- illegal with above restriction
-- missing instance error (maybe) delayed to user without
Simply set -XFlexibleContexts to disable the check.
As for other monads, I’m pretty sure Reader, State, and Free will do this too.

Related

Why does Haskell's Monad implementation fail function result differ from error's result?

I am trying to understand monads and reading it's type class definition:
class Monad m where
.
.
.
fail :: String -> m a
fail msg = error msg
Now, the definition of error is:
error :: [Char] -> a
Shouldn't the type system complain in this case? as
a /= m a
Or does the type system automatically assume that the results from error will be transformed into
m a
somehow?
Thanks in advance
The as in fail's and error's type signatures are a type variables. We can rename them without changing their meaning, e.g.
error :: [Char] -> b
error :: [Char] -> c
error :: [Char] -> d
All those type signatures have the same meaning; we just used alpha conversion on the type level.
Now we set b ~ m a, where m is fixed by fail's context and we see that error can get used:
fail :: String -> m a
fail msg = error msg -- error :: String -> b
-- b ~ m a

Haskell: 'tell ["abc"]' evaluation throwing "non-type variable argument" error

When I attempt to evaluate tell ["abc"] in GHCI, it spits out this error:
Non type-variable argument
in the constraint: MonadWriter [[Char]] m
(Use FlexibleContexts to permit this)
If I do tell ["abc"] :: Writer [String] (), it evalutes without a problem, but why is that necessary?
tell's return type is more general than Writer: it uses the MonadWriter type class to abstract over all monads with a writerly spirit.
tell :: MonadWriter w m => w -> m ()
So when you write tell ["abc"] with no type annotation, the type-checker unifies w with [String] but it doesn't have any information with which to refine m. So you get
tell ["abc"] :: MonadWriter [String] m => m ()
Haskell 98 doesn't support constraints like MonadWriter applied to concrete type arguments like [String], so the above is not valid Haskell 98. Turning on the FlexibleContexts language extension enables support for this sort of type.
Since Writer w is an instance of MonadWriter w (when w is an instance of Monoid), tell's principal type can be specialised to Writer. That's why it worked when you gave it the Writer type annotation.
tell :: w -> Writer w ()

Haskell: function signature

This program compiles without problems:
bar :: MonadIO m
=> m String
bar = undefined
run2IO :: MonadIO m
=> m String
-> m String
run2IO foo = liftIO bar
When I change bar to foo (argument name),
run2IO :: MonadIO m
=> m String
-> m String
run2IO foo = liftIO foo
I get:
Couldn't match type ‘m’ with ‘IO’
‘m’ is a rigid type variable bound by
the type signature for run2IO :: MonadIO m => m String -> m String
...
Expected type: IO String
Actual type: m String ...
Why are the 2 cases are not equivalent?
Remember the type of liftIO:
liftIO :: MonadIO m => IO a -> m a
Importantly, the first argument must be a concrete IO value. That means when you have an expression liftIO x, then x must be of type IO a.
When a Haskell function is universally quantified (using an implicit or explicit forall), then that means the function caller chooses what the type variable is replaced by. As an example, consider the id function: it has type a -> a, but when you evaluate the expression id True, then id takes the type Bool -> Bool because a is instantiated as the Bool type.
Now, consider your first example again:
run2IO :: MonadIO m => m Integer -> m Integer
run2IO foo = liftIO bar
The foo argument is completely irrelevant here, so all that actually matters is the liftIO bar expression. Since liftIO requires its first argument to be of type IO a, then bar must be of type IO a. However, bar is polymorphic: it actually has type MonadIO m => m Integer.
Fortunately, IO has a MonadIO instance, so the bar value is instantiated using IO to become IO Integer, which is okay, because bar is universally quantified, so its instantiation is chosen by its use.
Now, consider the other situation, in which liftIO foo is used, instead. This seems like it’s the same, but it actually isn’t at all: this time, the MonadIO m => m Integer value is an argument to the function, not a separate value. The quantification is over the entire function, not the individual value. To understand this more intuitively, it might be helpful to consider id again, but this time, consider its definition:
id :: a -> a
id x = x
In this case, x cannot be instantiated to be Bool within its definition, since that would mean id could only work on Bool values, which is obviously wrong. Effectively, within the implementation of id, x must be used completely generically—it cannot be instantiated to a specific type because that would violate the parametricity guarantees.
Therefore, in your run2IO function, foo must be used completely generically as an arbitrary MonadIO value, not a specific MonadIO instance. The liftIO call attempts to use the specific IO instance, which is disallowed, since the caller might not provide an IO value.
It is possible, of course, that you might want the argument to the function to be quantified in the same way as bar is; that is, you might want its instantiation to be chosen by the implementation, not the caller. In that case, you can use the RankNTypes language extension to specify a different type using an explicit forall:
{-# LANGUAGE RankNTypes #-}
run3IO :: MonadIO m => (forall m1. MonadIO m1 => m1 Integer) -> m Integer
run3IO foo = liftIO foo
This will typecheck, but it’s not a very useful function.
In the first, you're using liftIO on bar. That actually requires bar :: IO String. Now, IO happens to be (trivially) an instance on MonadIO, so this works – the compiler simply throws away the polymorphism of bar.
In the second case, the compiler doesn't get to decide what particular monad to use as the type of foo: it's fixed by the environment, i.e. the caller can decide what MonadIO instance it should be. To again get the freedom to choose IO as the monad, you'd need the following signature:
{-# LANGUAGE Rank2Types, UnicodeSyntax #-}
run2IO' :: MonadIO m
=> (∀ m' . MonadIO m' => m' String)
-> m String
run2IO' foo = liftIO foo
... however I don't think you really want that: you might then as well write
run2IO' :: MonadIO m => IO String -> m String
run2IO' foo = liftIO foo
or simply run2IO = liftIO.

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.

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