Stuck on design problem

All, Since I'm stuck on a coding problem and can't figure out how to proceed, I decided to call for help. I'm unable to get some code to typecheck, so maybe I'm missing something obvious on the implementation side, or more likely the design is completely wrong. Here's the idea: I have some code which uses some log of "Entry a" values (for some polymorphic type 'a'), and provides actions which output "Command a" values which should be interpreted by some wrapper code, e.g. adding some new entries to the log. These actions are implemented in a custom WriterT transformer (in this demo code, the original is more complex) to output Commands, while the inner monad should give access to the log entries (this could be in-memory like in the example, or stored on disk so using IO). You can find a trimmed-down version at https://gist.github.com/NicolasT/4230251f4f87f110d197 This doesn't type-check, and I'm not sure how to proceed. Am I taking a wrong approach? Do I need a different design? Thanks, Nicolas

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
Hope this helps, Adam On 20/05/13 12:25, Nicolas Trangez wrote:
All,
Since I'm stuck on a coding problem and can't figure out how to proceed, I decided to call for help.
I'm unable to get some code to typecheck, so maybe I'm missing something obvious on the implementation side, or more likely the design is completely wrong.
Here's the idea: I have some code which uses some log of "Entry a" values (for some polymorphic type 'a'), and provides actions which output "Command a" values which should be interpreted by some wrapper code, e.g. adding some new entries to the log. These actions are implemented in a custom WriterT transformer (in this demo code, the original is more complex) to output Commands, while the inner monad should give access to the log entries (this could be in-memory like in the example, or stored on disk so using IO).
You can find a trimmed-down version at https://gist.github.com/NicolasT/4230251f4f87f110d197
This doesn't type-check, and I'm not sure how to proceed. Am I taking a wrong approach? Do I need a different design?
Thanks,
Nicolas
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- The University of Strathclyde is a charitable body, registered in Scotland, with registration number SC015263.

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

:t runMemLog (runTransitionT $ demo 1)
runMemLog (runTransitionT $ demo 1)
:: MonadLog (MemLog a) () => Log a -> ((), [Command ()])
That means, that "foo", if you manage to compile it, would have type MonadLog (MemLog a) () => ((), [Command ()]). That means that in each call for foo it would be used as something of the type ((), [Command ()]), therefore giving no indication of which "a" should be used. The fact that there is only one instance of MonadLog (MemLog a) () - namely, the one with a = () - doesn't matter, as there could be others in the future. That's why GHC is complaining.
So, you have to provide such indication by youself, replacing IntMap.empty with (IntMap.empty :: Log ()).
20.05.2013, 15:36, "Nicolas Trangez"
All,
Since I'm stuck on a coding problem and can't figure out how to proceed, I decided to call for help.
I'm unable to get some code to typecheck, so maybe I'm missing something obvious on the implementation side, or more likely the design is completely wrong.
Here's the idea: I have some code which uses some log of "Entry a" values (for some polymorphic type 'a'), and provides actions which output "Command a" values which should be interpreted by some wrapper code, e.g. adding some new entries to the log. These actions are implemented in a custom WriterT transformer (in this demo code, the original is more complex) to output Commands, while the inner monad should give access to the log entries (this could be in-memory like in the example, or stored on disk so using IO).
You can find a trimmed-down version at https://gist.github.com/NicolasT/4230251f4f87f110d197
This doesn't type-check, and I'm not sure how to proceed. Am I taking a wrong approach? Do I need a different design?
Thanks,
Nicolas
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Miguel, On Mon, 2013-05-20 at 16:38 +0400, Miguel Mitrofanov wrote:
:t runMemLog (runTransitionT $ demo 1) runMemLog (runTransitionT $ demo 1) :: MonadLog (MemLog a) () => Log a -> ((), [Command ()])
That means, that "foo", if you manage to compile it, would have type MonadLog (MemLog a) () => ((), [Command ()]). That means that in each call for foo it would be used as something of the type ((), [Command ()]), therefore giving no indication of which "a" should be used. The fact that there is only one instance of MonadLog (MemLog a) () - namely, the one with a = () - doesn't matter, as there could be others in the future. That's why GHC is complaining.
So, you have to provide such indication by youself, replacing IntMap.empty with (IntMap.empty :: Log ()).
Thanks for the pointer. I tried using an explicit type-annotation myself before as well, but that failed, and I found the root-cause: in my original code, the type-signature of runTransitionT was all wrong, instead of runTransitionT :: TransitionT a m a -> m (a, [Command a]) this was intended to be runTransitionT :: TransitionT a m r -> m (r, [Command a]) After making this change, and using an explicit type-signature for the empty IntMap as you suggested, things work as expected, see https://gist.github.com/NicolasT/4230251f4f87f110d197/revisions In my original code, this didn't help out somehow (some other things at play there as well), but another suggestion on this list did. Thanks for the help! Nicolas
20.05.2013, 15:36, "Nicolas Trangez"
: All,
Since I'm stuck on a coding problem and can't figure out how to proceed, I decided to call for help.
I'm unable to get some code to typecheck, so maybe I'm missing something obvious on the implementation side, or more likely the design is completely wrong.
Here's the idea: I have some code which uses some log of "Entry a" values (for some polymorphic type 'a'), and provides actions which output "Command a" values which should be interpreted by some wrapper code, e.g. adding some new entries to the log. These actions are implemented in a custom WriterT transformer (in this demo code, the original is more complex) to output Commands, while the inner monad should give access to the log entries (this could be in-memory like in the example, or stored on disk so using IO).
You can find a trimmed-down version at https://gist.github.com/NicolasT/4230251f4f87f110d197
This doesn't type-check, and I'm not sure how to proceed. Am I taking a wrong approach? Do I need a different design?
Thanks,
Nicolas
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
participants (3)
-
Adam Gundry
-
Miguel Mitrofanov
-
Nicolas Trangez