Closed johnsonwj closed 4 years ago
I have been poking around the docs for type families - maybe associated type families will be enough to represent these constraints, e.g.
class Monad m => MonadState m where
data S m
get :: m (S m)
put :: S m -> m ()
This would avoid "mixing flavors" with fundeps, since it seems that the same type-level logic can be represented in this way, and eff
already makes effective (sorry) use of type families internally. I just need to tinker around more with Control.Effect.Internal
- much of this is still new and mysterious for me, so unless it's obvious to anyone else whether or not that is even possible, I'll be seeing if that works!
After playing around some time with this library myself, I came to a similar conclusion: Functional dependencies break the automatic lifting machinery, and I think it is impossible to fix it in its current form.
As far as I understand it, the automatic lifting machinery works in a way that says: "Every transformer stack t m a
can handle an effect, it is only the question if t m
handles it or m
handles it." This is not compatible with a functional dependency m -> s
, i.e. you cannot convince the type system to infer s
from a generic (see "every" above) transformer stack EffT t m
(note that s
is not part of the type). As a result, you get the coverage condition failure as described.
This is very unfortunate, because that means you also cannot have tagged effects like ...
class Monad m => TaggedState tag s m | m tag -> s where
get :: m s
put :: s -> m ()
... which would improve type inference and disambiguation of effects.
But I am not an expert in all of this, so maybe there is a solution, but I just don't see it.
I’m going to close this issue, as it no longer makes sense under the rewrite, as effects are not typeclasses anymore. You might be able to get what you want under the new implementation by defining a custom typeclass (with functional dependencies) on the type-level list of effects, with :<
as a superclass, but I haven’t tried it myself.
Even though this is closed, I imagine the "solution" will be to implement new type classes with FDs (per effect that needs FDs), have a superclass constraint of requiring :<, but we probably need to have a play and see if that works
Yes, that is what I just suggested.
Great, everyone's on the same page then!
Heyo! I apologize in advance that this may end up being silly because I confess that I am still pretty new to most GHC extensions, but I have been racking my brain over the language option docs in the GHC user guide and I can't seem to get anywhere.
TLDR: Is it possible to use functional dependencies in classes like
State s m
orReader r m
or is that not feasible in the design of this library?I saw the announcement for this library on reddit and decided to try to use it to do stuff. Here's a basic example that fails to compile without
AllowAmbiguousTypes
:If I don't include the pragma, I get the following error:
which is presumably because GHC has no reason to believe that an arbitrary
m ()
could possibly satisfy the constraints.Moreover, even with that flag, I need to sprinkle around type applications; otherwise...
Here's the same thing in
mtl
; in particular, I no longer needAllowAmbiguousTypes
, and I can even get away with leaving out the type applications:Assuming that I'm just bad at type magic, I asked over at the Haskell discourse if anyone could spot what I was missing. artem's comment pointed me to a possible solution: namely that the
State
class ineff
does not specify a functional dependencym -> s
.To my eye this would make sense; whatever monad we happen to be in should be able to fix the type of state that it's holding. But if I just naively try to add that dependency,
eff
no longer compiles; specifically, changing this line toclass Monad m => State s m | m -> s where
yieldsFeeling around
Internal.hs
I also added| m -> eff
here and here, and| m -> effs
here. Now I get:At this point I am thoroughly out of my league with type families; my guess is that the solution has to do with type family dependencies but I am not sure what that would look like. Based on comparing the examples in the doc to what I see here, there is too much going on that I don't yet get :(
If functional dependencies are simply not possible, is the use of
AllowAmbiguousTypes
for this sort of thing unavoidable? Or is there another option I am missing?Thanks for any advice!!