
Adam, On Mon, 2013-05-20 at 13:37 +0100, Adam Gundry wrote:
Hi Nicolas,
Your design doesn't look too unreasonable, though I haven't looked at in detail. I do have a quick observation regarding the implementation that I hope might help. Your gist defines
class MonadLog m a where getEntry :: Index -> m (Entry a)
instance MonadLog (MemLog a) a
and then you hit the error
No instance for (MonadLog (MemLog a0) ())
which is a tell-tale ambiguity problem. This is a common issue with multi-parameter type classes. GHC cannot determine the existential variable a0, because it will not commit to the instance for MemLog in case a more specific instance turns up.
One option is to turn on GADTs or TypeFamilies and write
instance a ~ a' => MonadLog (MemLog a) a'
which will allow the instance to match and generate the easily solved constraint a0 ~ (). You may need to do the same for other instances.
Alternatively, you could add a functional dependency
class MonadLog m a | m -> a
or use a type family:
class MonadLog' m where type Element m getEntry' :: Index -> m (Entry (Element m))
instance MonadLog' (MemLog a) where type Element (MemLog a) = a getEntry' i = flip (IntMap.!) i `fmap` ask
Thanks a bunch. Using FunDeps, things work as expected, also in my more complicated original code. I chose the FunDeps approach because it seems the least intrusive (no need to clutter instances). Thanks a bunch for all help, this list is certainly one of the things which makes writing Haskell code a very rewarding experience. Thanks, Nicolas