Let's assume four functions f1,f2,f3,f4 :: SomeMonad m => m a -> m a where f1 and f2 are conceptually related and similarly f3 and f4.
Now it is possible to write a function f :: SomeMonad m => (m a -> m a) -> m a that can take any of the f1..f4 as input. And then it is possible to write, say, f1 $ f f2.
The question is, how to write a function g such that it accepts only f1 or f2 as a parameter? We should be able to write f4 $ g f1 and ghc should deny g f3.
If I try to add a class, say :: (SomeMonad m, Some_G_class a) =>, the return type a is also constrained and then f4 cannot be applied. And similarly it seems that the m cannot be modified.
Late Addition
The answers by Daniel Wagner and leftroundabout both seem to be useable.
Here is more about the actual (and practical) problem.
- The underlying lib contains tens of functions returning
m a. - The lib has been build so that functions can be chained, e.g. with
(.)likef1 . f2 . f3. - Conceptually, there is about five to ten subsets of those functions, subsets overlapping. The functions should be chaineable within each subset but not with functions from the other subsets.
Using Proxies
It seems that
data Type = A | B | C ... | H
data Proxy (ty :: Type) = Proxy
could be used to classify functions. To handle overlapping useage, a function call like
h1 :: SomeMonad t m => Either (Proxy A) (Proxy B) -> m t -> m t
h1 (Left (Proxy :: Proxy A) mt = ..
h1 (Right (Proxy :: Proxy B) mt = ..
could do it. But what if there are functions belonging to A, B, C and D? Maybe there is something like OneOf or Any that would work here?
Using RankNTypes
It seems that the SpecificMonad-constraint (restriction?) starts to propage to the usage sites of the functions f3 and f4 (that is, all over). I feel that if I try to manage the 5-10 overlapping function sets, there will be serious difficulties with those constraints propagating every where. Is this the case?
So we need a function to make coercing, maybe something like
fg :: (SomeMonad t m1, SpecificMonad t m2) => m2 t -> m1 t
fg a = pure $ fromSpecM a
and atm assuming SomeMonad has pure. In my case I cannot add fromSpecM to the class-methods of SomeMonad but maybe it is ok in SpecificMonad. And my first trials along this way are still too simple so that I could see if this is doable. It may well be that I'm not seeing something very obvious here.
In a way, SpecificMonad shouldn't have any additional methods and it should just work as a "local env/fix/hack" not affecting other parts of the code. Thus, is there other ways to accomplish something like fg?
m a.f1with another one of the same type likef4is always possible. You need to assign those two functions different types, e.g. using anewtypewrapper.