I don't have quite the vocabulary for phrasing this question (and therefore for searching for answers, so apologies if the answer is easily available). Consider the following
class RunFoo m where
runFoo :: m a -> a
class RunFooWrapper m where
doRunFoo :: (RunFoo n) => n a -> m a
newtype RunFast a = RunFast a
newtype RunSlow a = RunSlow a
fooExample :: (RunFoo m) => m Bool
fooExample = undefined
fooWrapperExample :: (RunFooWrapper m) => m Bool
fooWrapperExample = doRunFoo fooExample
This does not compile: Could not deduce (RunFoo n0) arising from a use of ‘doRunFoo’
.
It seems that the compiler (GHC 7.10) is insisting on a concrete instantiation of the m
from fooExample
, and is therefore refusing to continue. But in this case I can't see why the program is ill-typed - fooExample
explicitly defines a RunFoo m
and all doRunFoo
requires is a RunFoo x
. So why doesn't this work?
As a supplementary question, is there some kind of special extension (something to do with existential types perhaps) that would allow a program like this? Ideally I'd like to be able to say that doRunFoo takes anything defined existentially (?) as RunFoo m => m
(rather than taking any concrete instantiation of RunFoo
).
Motivation
I'd like to create composable functions that operate on some aspect of a type constrained by a typeclass - I can provide a concrete example of what I mean if necessary!
Edit with more motivation and alternative implementation
I was curious about the answer to this question in the general case, but I thought I'd add that in the context I ran across this issue what I really wanted is for a sort of constrained delegation between monads. So I wanted to write functions in a monad constrained only by a typeclass that invoked monads in another type class. The top level function could then be run in different contexts, performing the same logic but with the underlying implementation swapped out according to the wrapping monad. As there was a one-to-one correspondence between the wrapping and the implementation monad I was able to use type families to do this, so
class (RunFoo (RunFooM m)) => RunFooWrapper m where
type RunFooM m :: * -> *
doRunFoo :: RunFooM m a -> m a
instance RunFooWrapper RunFooWrapperSlow where
type RunFooM RunFooWrapperSlow = RunSlow
doRunFoo :: [...]
This meant that the resolution of the fooExample
m
was determined by the class context of the wrapper monad, and seems to work fine, but it is a very narrow solution compared to that provided by haoformayor.
show . read
problem. You need to pick a typen
fordoRunFoo
. – Reid BartonfooWrapperExample
specify which instance ofRunFoo
they want you to use infooExample
? – amalloy