How to abstract over monads without fighting the type system in Haskell? - 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.

Related

Adding a Constraint to a Monad Result Type

I would like to create a type that is an instance of Monad such that the result type can only be an instance of a specific typeclass. I would like to be able to write something like
data T a = T a
class C a where
...
instance Monad T where
return :: (C a) => a -> m a
return x = ...
(>>=) :: (C a, C b) => m a -> (a -> m b) -> m b
p >>= f = ...
In the actual code that I'm working on, I need a typeclass constraint on the result type so that a specific function from the typeclass is available in the definitions for return and (>>=).
Is there any way to do this?
As per #chi's comment, the best solution is probably to use either constrained monads or indexed monads. Both solutions have the caveat of having to work around the standard library, but it can be done with workarounds such as rebinding syntax using the RebindableSyntax extension for example. The best example of constrained monads I was able to find was the constrained-monads package available on Hackage, while the best example I was able to find of indexed monads was a post at Kwang's Haskell Blog.

How can I let Haskell know I don't care what a generic's type is?

I'm working on some code deep in the bowels of an application and would like to make it generic w.r.t. some of the types it's using so I can mock the types for unit tests.
I'm trying to setup a type-class along the lines of:
class (Monad m) => SomeClass m a x d | m -> a where
makeState :: x -> m (a x)
open :: (a x) -> m (a d)
use :: OtherArg -> (a d) -> m ()
close :: (a d) -> m ()
where the m type-variable will be a monad (e.g. IO, ST), a will be some sort of wrapper type in the monad (e.g. MVar, STRef) and x will be known at compile-time, but I don't care about the type of d in the functions using the type-class, just in the type-class instance function implementations themselves.
Right now I'm just trying to get my types setup and it's erroring when I add the constraint to my functions, e.g.:
someFn :: (SomeClass m a Model d) => a d -> a Model -> T m ()
the compiler is complaining that d is ambiguous and won't let me get away without concretely specifying the type, meaning I won't be able to mock it.
I looked briefly at some language extensions and it seems like RankNTypes might be what I need but I'm not sure how to go about utilizing it.
Is this the right approach to accomplish what I'm trying to do, or should I be approaching it differently? (I wouldn't be surprised if I'm approaching it too OOPily as I've been working in Java a lot recently.)
Thanks in advance.
Perhaps it is enough make the fundep mad:
class (Monad m) => SomeClass m a x d | m -> a d where

Type variable is not in scope when defining type class

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.

Class contraints for monads and monad functions

I am trying to write a new monad that only can contain a Num. When it fails, it returns 0 much like the Maybe monad returns Nothing when it fails.
Here is what I have so far:
data (Num a) => IDnum a = IDnum a
instance Monad IDnum where
return x = IDnum x
IDnum x >>= f = f x
fail :: (Num a) => String -> IDnum a
fail _ = return 0
Haskell is complaining that there is
No instance for (Num a) arising from a use of `IDnum'
It suggests that I add a add (Num a) to the context of the type signature for each of my monad functions, but I tried that it and then it complains that they need to work "forall" a.
Ex:
Method signature does not match class; it should be
return :: forall a. a -> IDnum a
In the instance declaration for `Monad IDnum'
Does anyone know how to fix this?
The existing Monad typeclass expects your type to work for every possible type argument. Consider Maybe: in Maybe a, a is not constrained at all. Basically you can't have a Monad with constraints.
This is a fundamental limitation of how the Monad class is defined—I don't know of any way to get around it without modifying that.
This is also a problem for defining Monad instances for other common types, like Set.
In practice, this restriction is actually pretty important. Consider that (normally) functions are not instances of Num. This means that we could not use your monad to contain a function! This really limits important operations like ap (<*> from Applicative), since that depends on a monad containing a function:
ap :: Monad m => m (a -> b) -> m a -> m b
Your monad would not support many common uses and idioms we've grown to expect from normal monads! This would rather limit its utility.
Also, as a side-note, you should generally avoid using fail. It doesn't really fit in with the Monad typeclass: it's more of a historic accident. Most people agree that you should avoid it in general: it was just a hack to deal with failed pattern matches in do-notation.
That said, looking at how to define a restricted monad class is a great exercise for understanding a few Haskell extensions and learning some intermediate/advanced Haskell.
Alternatives
With the downsides in mind, here are a couple of alternatives—replacements for the standard Monad class that do support restricted monads.
Constraint Kinds
I can think of a couple of possible alternatives. The most modern one would be taking advantage of the ConstraintKind extension in GHC, which lets you reify typeclass constraints as kinds. This blog post details how to implement a restricted monad using constraint kinds; once I've read it, I'll summarize it here.
The basic idea is simple: with ConstraintKind, we can turn our constrain (Num a) into a type. We can then have a new Monad class which contains this type as a member (just like return and fail are members) and allows use to overload the constraint with Num a. This is what the code looks like:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
module Main where
import Prelude hiding (Monad (..))
import GHC.Exts
class Monad m where
type Restriction m a :: Constraint
type Restriction m a = ()
return :: Restriction m a => a -> m a
(>>=) :: Restriction m a => m a -> (a -> m b) -> m b
fail :: Restriction m a => String -> m a
data IDnum a = IDnum a
instance Monad IDnum where
type Restriction IDnum a = Num a
return = IDnum
IDnum x >>= f = f x
fail _ = return 0
RMonad
There is an existing library on hackage called rmonad (for "restricted monad") which provides a more general typeclass. You could probably use this to write your desired monad instance. (I haven't used it myself, so it's a bit hard to say.)
It doesn't use the ConstraintKinds extension and (I believe) supports older versions of GHC. However, I think it's a bit ugly; I'm not sure that it's the best option any more.
Here's the code I came up with:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
import Prelude hiding (Monad (..))
import Control.RMonad
import Data.Suitable
data IDnum a = IDnum a
data instance Constraints IDnum a = Num a => IDnumConstraints
instance Num a => Suitable IDnum a where
constraints = IDnumConstraints
instance RMonad IDnum where
return = IDnum
IDnum x >>= f = f x
fail _ = withResConstraints $ \ IDnumConstraints -> return 0
Further Reading
For more details, take a look at this SO question.
Oleg has an article about this pertaining specifically to the Set monad, which might be interesting: "How to restrict a monad without breaking it".
Finally, there are a couple of papers you could also read:
The Constrained-Monad Problem
Generic Monadic Constructs for Embedded Languages
This answer will be brief, but here's another alternative to go along with Tikhon's. You can apply a codensity transformation to your type to basically get a free monad for it. Just use it (in the below code it's IDnumM) instead of your base type, then convert the final value to your base type at the end (in the below code, you would use runIDnumM). You can also inject your base type into the transformed type (in the below code, that would be toIDnumM).
A benefit of this approach is that it works with the standard Monad class.
data Num a => IDnum a = IDnum a
newtype IDnumM a = IDnumM { unIDnumM :: forall r. (a -> IDnum r) -> IDnum r }
runIDnumM :: Num a => IDnumM a -> IDnum a
runIDnumM (IDnumM n) = n IDnum
toIDnumM :: Num a => IDnum a -> IDnumM a
toIDnumM (IDnum x) = IDnumM $ \k -> k x
instance Monad IDnumM where
return x = IDnumM $ \k -> k x
IDnumM m >>= f = IDnumM $ \k -> m $ \x -> f x `unIDnumM` k
There is an easier way to do this. One can use multiple functions. First, write one in the Maybe monad. The Maybe monad returns Nothing upon failure. Second, write a function that returns the Just value if not Nothing or some safe value if Nothing. Third, write a function that composes those two functions.
This produces the desired result while being much easier to write and understand.

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.

Resources