Type variable is not in scope when defining type class - haskell

I'm writing a type class à la mtl-style transformers. Looks like this:
class (Monad m, Stream s m t) => MonadStuff s m | m -> s where
-- function signatures go here…
I'm trying to say by that that m should be instance of Monad and there
should be instance of Stream s m t, where t doesn't really matter but
s and m are from the right side of the definition (after =>).
Haskell says:
Not in scope: type variable ‘t’
So, obviously I cannot do that. Or can I? Should I remove Stream s m t
constraint and add it to every function in the class instead or is there
another way?

If it's really true that it doesn't really matter what t is, then perhaps you can ask the person writing the instance to choose it:
{-# LANGUAGE TypeFamilies #-}
class (Monad m, Stream s m (StuffType m)) => MonadStuff s m | m -> s where
type StuffType m
Or, since you already have MPTCs and fundeps turned on, you could consider doing this, which requires no extra extensions but is otherwise basically identical:
class (Monad m, Stream s m t) => MonadStuff s m t | m -> s t where
However, I am suspicious that in fact the choice of t does matter: unless Stream has a fundep that is at least as informative as m s -> t, you will not be able to use this constraint in a meaningful way. In that case, you should move the constraint into the signatures of the methods that mention t or will be using the Stream methods.

Related

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.

How to avoid the need of an auxiliary newtype when matching this Backpack signature?

I'm using the new Backpack module system along with Cabal 2. I have the follwing signature:
{-# LANGUAGE KindSignatures #-}
signature Streamy where
import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.IO.Class
data Stream :: * -> (* -> *) -> * -> *
instance Monad m => Functor (Stream o m)
instance Monad m => Applicative (Stream o m)
instance Monad m => Monad (Stream o m)
instance MonadIO m => MonadIO (Stream o m)
instance MonadTrans (Stream o)
I'm trying to match it with the concrete Stream type from streaming, like this:
import Streaming (Of(..))
import qualified Streaming as Q
type Stream o m r = Q.Stream (Of o) m r
However, this gives me the error
Illegal parameterized type synonym in implementation of abstract data.
(Try eta reducing your type synonym so that it is nullary)
It seems that the type synonym can't have parameters. I need at least one however, because the o is sitting inside the Of instead of being a direct parameter of Q.Stream.
I can solve the problem by defining an adapter newtype:
{-# language GeneralizedNewtypeDeriving #-}
newtype S o m r = S { unS :: Q.Stream (Of o) m r }
deriving (Functor,Applicative,Monad,MonadIO,MonadTrans)
type Stream = S
Is there another way that doesn't require defining the newtype?
It seems that there isn't a way to avoid the newtype. The Backpack thesis clearly states that type synonyms are required to be nullary (section 6.2.4 "Subtyping rules"):
the implementing type synonyms are required to be nullary: for
example, type M a = a is not a valid implementation of the abstract
data M a, but type M = Maybe :: * -> * is. This restriction stems
from an old design decision in Haskell to not support type level
lambdas. This restriction greatly helps type inference, since given
the type equality t a = s b, we can now conclude that t = s and a = b
(this property is called generativity).
Also related, in section B.2 "Challenges":
Translucency: A feature not supported in Backpack’14 but supported in
Backpack’17 is the ability to use a type synonym to implement an
abstract data type. This introduces a form of “translucency”, where
the abstract data type is opaque while the implementation is unknown,
and transparent afterwards. For example, in Section 2.3, we
demonstrated how we could instantiate a generic regular expression
matcher to match on strings. Inside the implementation of the matcher,
we knew nothing about the abstract type Str; after instantiating it,
the accept function transparently accepts String arguments.

Why is Identity monad useful?

I often read that
It seem that identity monad is useless. It's not... but that's another
topic.
So can anyone tell my how is it useful?
Identity is to monads, functors and applicative functors as 0 is to numbers. On its own it seems useless, but it's often needed in places where one expects a monad or an (applicative) functor that actually doesn't do anything.
As already mentioned, Identity allows us to define just monad transformers and then define their corresponding monads just as SomeT Identity.
But that's not all. It's often convenient to also define other concepts in terms of monads, which usually adds a lot of flexibility. For example Conduit i m o (also see this tutorial) defines an element in a pipeline that can request data of type i, can produce data of type o, and uses monad m for internal processing. Then such a pipeline can be run in the given monad using
($$) :: Monad m => Source m a -> Sink a m b -> m b
(where Source is an alias for Conduit with no input and Sink for Conduit with no output). And when no effectful computations are needed in the pipeline, just pure code, we just specialize m to Identity and run such a pipeline as
runIdentity (source $$ sink)
Identity is also the "empty" functor and applicative functor: Identity composed with another functor or applicative functor is isomorphic to the original. For example, Lens' is defined as a function polymorphic in a Functor:
Functor f => (a -> f a) -> s -> f s
roughly speaking, such a lens allows to read or manipulate something of type a inside s, for example a field inside a record (for an introduction to lenses see this post). If we specialize f to Identity, we get
(a -> Identity a) -> s -> Identity s
which is isomorphic to
(a -> a) -> s -> s
so given an updating function on a, return an updating function on s. (For completeness: If we specialize f to Const a, we get (a -> Const b a) -> s -> Const b s, which is isomorphic to (a -> b) -> (s -> b), that is, given a reader on a, return a reader on s.)
Sometimes I work with records whose fields are optional in some contexts (like when parsing the record from JSON) but mandatory in others.
I solve that by parametrizing the record with a functor, and using Maybe or Identity in each case.
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE StandaloneDeriving #-}
data Query f = Query
{
_viewName :: String
, _target :: f Server -- Server is some type, it doesn't matter which
}
deriving (Generic)
The server field is optional when parsing JSON:
instance FromJSON (Query Maybe)
But then I have a function like
withDefaultServer :: Server -> Query Maybe -> Query Identity
withDefaultServer = undefined
that returns a record in which the _target field is mandatory.
(This answer doesn't use anything monadic about Identity, though.)
One use of it is as a base monad for monad transformer stacks: instead of having to provide two types Some :: * ->* and SomeT :: (* -> *) -> * -> *, it is enough to provide just a latter by setting type Some = SomeT Identity.
Another, somewhat similar use case (but completely detached from the whole monad business) is when you need to refer to tuples: we can say () is a nullary tuple, (a, b) is a binary tuple, (a, b, c) is a ternary tuple, and so on, but what does that leave for the unary case? Saying a is a unary tuple for any choice of a is often not satisfactory, for example when we are building some typeclass instances like Data.Tuple.Select, some type constructor is needed to act as the unambiguous key. So by adding e.g. Sel1 instances to Identity a, it forces us to distinguish between (a, b) (a two-tuple containing an a and a b), and Identity (a, b) (a one-tuple containing a single (a, b) value).
(Note that Data.Tuple.Select defines its own type called OneTuple instead of reusing Identity, but it is isomorphic to Identity—in fact, it's just a rename away—and I think it only exists to avoid a non-base dependency.)
One real use-case is to be a (pure) base of monad transformers stack, e.g.
type Reader r = ReaderT r Identity

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.

Why aren't monad transformers constrained to yield monads?

In the MonadTrans class:
class MonadTrans t where
-- | Lift a computation from the argument monad to the constructed monad.
lift :: Monad m => m a -> t m a
why isn't t m constrained to be a Monad? i.e., why not:
{-# LANGUAGE MultiParamTypeClasses #-}
class Monad (t m) => MonadTrans t m where
lift :: Monad m => m a -> t m a
If the answer is "because that's just the way it is", that's fine -- it's just confusing for a n008.
You suggested the following:
class Monad (t m) => MonadTrans t m where
lift :: Monad m => m a -> t m a
...but does that really mean what you want? It seems you want to express something like "a type t may be an instance of MonadTrans if, for all m :: * -> * where m is an instance of Monad, t m is also an instance of Monad".
What the class definition above actually says is more like "types t and m may constitute an instance of MonadTrans if, for those specific types, t m is an instance of Monad". Consider carefully the difference, and the implied potential for instances that may not be what you'd want.
In the general case, every parameter of a type class is an independent "argument", a fact which has been a bountiful source of both headaches and GHC extensions as people have attempted to use MPTCs.
Which isn't to say that such a definition couldn't be used anyway--as you point out, the current definition is not ideal either. The age-old problem "Why Data.Set Is Not a Functor" is related, and such issues helped motivate the recent ConstraintKinds tomfoolery.
The ultimate answer to "why not" here is almost certainly the one given by Daniel Fischer in the comments--because MonadTrans is pretty core functionality, it would be undesirable to make it depend on some terrifying cascade of increasingly arcane GHC extensions.

Resources