Overlapping instances - how to circumvent them











up vote
2
down vote

favorite
1












I am a beginner at Haskell, so please be indulgent. For reasons that are not important here, I am trying to define a operator <^> that takes a function and an argument and returns the value of the function by the argument, irrespective of which of the function and the argument came first. In short, I would like to be able to write the following:



foo :: Int -> Int
foo x = x * x

arg :: Int
arg = 2

foo <^> arg -- valid, returns 4
arg <^> foo -- valid, returns 4


I have tried to accomplish that through type families, as follows:



{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies, TypeOperators #-}

class Combine t1 t2 where
type Output t1 t2 :: *
(<^>) :: t1 -> t2 -> Output t1 t2

instance Combine (a->b) a where
type Output (a->b) a = b
f <^> x = f x

instance Combine a (a->b) where
type Output a a->b = b
x <^> f = f x


On this code, GHC throws a Conflicting family instance declarations. My guess is that the overlap GHC complains about occurs when type a->b and type a are the same. I don't know Haskell well enough, but I suspect that with recursive type definitions, one may be able to construct such a situation. I have a couple of questions:




  • Since this is a rather remote scenario that will never occur in my application (in particular not with foo and arg above), I was wondering if there was a way of specifying a dummy default instance to use in case of overlap? I have tried the different OVERLAPS and OVERLAPPING flags, but they didn't have any effect.

  • If not, is there a better way of achieving what I want?


Thanks!










share|improve this question


















  • 2




    I don't think your class makes sense. const id and id const are both valid code, so what would const <^> id do?
    – melpomene
    Nov 18 at 13:39










  • wrt your "reasons that are not important", there's a serious use case here: some people want to use (.) operator for postfix function apply, as in most OOP. But there's too much legacy code using it as compose. Can we do both, by looking at the types of the arguments to (.)? There's still the const.id problem.
    – AntC
    Nov 19 at 11:11















up vote
2
down vote

favorite
1












I am a beginner at Haskell, so please be indulgent. For reasons that are not important here, I am trying to define a operator <^> that takes a function and an argument and returns the value of the function by the argument, irrespective of which of the function and the argument came first. In short, I would like to be able to write the following:



foo :: Int -> Int
foo x = x * x

arg :: Int
arg = 2

foo <^> arg -- valid, returns 4
arg <^> foo -- valid, returns 4


I have tried to accomplish that through type families, as follows:



{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies, TypeOperators #-}

class Combine t1 t2 where
type Output t1 t2 :: *
(<^>) :: t1 -> t2 -> Output t1 t2

instance Combine (a->b) a where
type Output (a->b) a = b
f <^> x = f x

instance Combine a (a->b) where
type Output a a->b = b
x <^> f = f x


On this code, GHC throws a Conflicting family instance declarations. My guess is that the overlap GHC complains about occurs when type a->b and type a are the same. I don't know Haskell well enough, but I suspect that with recursive type definitions, one may be able to construct such a situation. I have a couple of questions:




  • Since this is a rather remote scenario that will never occur in my application (in particular not with foo and arg above), I was wondering if there was a way of specifying a dummy default instance to use in case of overlap? I have tried the different OVERLAPS and OVERLAPPING flags, but they didn't have any effect.

  • If not, is there a better way of achieving what I want?


Thanks!










share|improve this question


















  • 2




    I don't think your class makes sense. const id and id const are both valid code, so what would const <^> id do?
    – melpomene
    Nov 18 at 13:39










  • wrt your "reasons that are not important", there's a serious use case here: some people want to use (.) operator for postfix function apply, as in most OOP. But there's too much legacy code using it as compose. Can we do both, by looking at the types of the arguments to (.)? There's still the const.id problem.
    – AntC
    Nov 19 at 11:11













up vote
2
down vote

favorite
1









up vote
2
down vote

favorite
1






1





I am a beginner at Haskell, so please be indulgent. For reasons that are not important here, I am trying to define a operator <^> that takes a function and an argument and returns the value of the function by the argument, irrespective of which of the function and the argument came first. In short, I would like to be able to write the following:



foo :: Int -> Int
foo x = x * x

arg :: Int
arg = 2

foo <^> arg -- valid, returns 4
arg <^> foo -- valid, returns 4


I have tried to accomplish that through type families, as follows:



{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies, TypeOperators #-}

class Combine t1 t2 where
type Output t1 t2 :: *
(<^>) :: t1 -> t2 -> Output t1 t2

instance Combine (a->b) a where
type Output (a->b) a = b
f <^> x = f x

instance Combine a (a->b) where
type Output a a->b = b
x <^> f = f x


On this code, GHC throws a Conflicting family instance declarations. My guess is that the overlap GHC complains about occurs when type a->b and type a are the same. I don't know Haskell well enough, but I suspect that with recursive type definitions, one may be able to construct such a situation. I have a couple of questions:




  • Since this is a rather remote scenario that will never occur in my application (in particular not with foo and arg above), I was wondering if there was a way of specifying a dummy default instance to use in case of overlap? I have tried the different OVERLAPS and OVERLAPPING flags, but they didn't have any effect.

  • If not, is there a better way of achieving what I want?


Thanks!










share|improve this question













I am a beginner at Haskell, so please be indulgent. For reasons that are not important here, I am trying to define a operator <^> that takes a function and an argument and returns the value of the function by the argument, irrespective of which of the function and the argument came first. In short, I would like to be able to write the following:



foo :: Int -> Int
foo x = x * x

arg :: Int
arg = 2

foo <^> arg -- valid, returns 4
arg <^> foo -- valid, returns 4


I have tried to accomplish that through type families, as follows:



{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies, TypeOperators #-}

class Combine t1 t2 where
type Output t1 t2 :: *
(<^>) :: t1 -> t2 -> Output t1 t2

instance Combine (a->b) a where
type Output (a->b) a = b
f <^> x = f x

instance Combine a (a->b) where
type Output a a->b = b
x <^> f = f x


On this code, GHC throws a Conflicting family instance declarations. My guess is that the overlap GHC complains about occurs when type a->b and type a are the same. I don't know Haskell well enough, but I suspect that with recursive type definitions, one may be able to construct such a situation. I have a couple of questions:




  • Since this is a rather remote scenario that will never occur in my application (in particular not with foo and arg above), I was wondering if there was a way of specifying a dummy default instance to use in case of overlap? I have tried the different OVERLAPS and OVERLAPPING flags, but they didn't have any effect.

  • If not, is there a better way of achieving what I want?


Thanks!







haskell






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 18 at 13:37









Ahmad B

133




133








  • 2




    I don't think your class makes sense. const id and id const are both valid code, so what would const <^> id do?
    – melpomene
    Nov 18 at 13:39










  • wrt your "reasons that are not important", there's a serious use case here: some people want to use (.) operator for postfix function apply, as in most OOP. But there's too much legacy code using it as compose. Can we do both, by looking at the types of the arguments to (.)? There's still the const.id problem.
    – AntC
    Nov 19 at 11:11














  • 2




    I don't think your class makes sense. const id and id const are both valid code, so what would const <^> id do?
    – melpomene
    Nov 18 at 13:39










  • wrt your "reasons that are not important", there's a serious use case here: some people want to use (.) operator for postfix function apply, as in most OOP. But there's too much legacy code using it as compose. Can we do both, by looking at the types of the arguments to (.)? There's still the const.id problem.
    – AntC
    Nov 19 at 11:11








2




2




I don't think your class makes sense. const id and id const are both valid code, so what would const <^> id do?
– melpomene
Nov 18 at 13:39




I don't think your class makes sense. const id and id const are both valid code, so what would const <^> id do?
– melpomene
Nov 18 at 13:39












wrt your "reasons that are not important", there's a serious use case here: some people want to use (.) operator for postfix function apply, as in most OOP. But there's too much legacy code using it as compose. Can we do both, by looking at the types of the arguments to (.)? There's still the const.id problem.
– AntC
Nov 19 at 11:11




wrt your "reasons that are not important", there's a serious use case here: some people want to use (.) operator for postfix function apply, as in most OOP. But there's too much legacy code using it as compose. Can we do both, by looking at the types of the arguments to (.)? There's still the const.id problem.
– AntC
Nov 19 at 11:11












2 Answers
2






active

oldest

votes

















up vote
1
down vote



accepted










This is a bad idea, in my view, but I'll play along.



A possible solution is to switch to functional dependencies. Usually I tend to avoid fundeps in favor of type families, but here they make the instances compile in a simple way.



class Combine t1 t2 r | t1 t2 -> r where
(<^>) :: t1 -> t2 -> r

instance Combine (a->b) a b where
f <^> x = f x

instance Combine a (a->b) b where
x <^> f = f x


Note that this class will likely cause problems during type inference if we use polymorphic functions. This is because, with polymorphic functions, the code can easily become ambiguous.



For instance id <^> id could pick any of the two instances. Above, melpomene already reported const <^> id being ambiguous as well.





The following is weakly related, but I want to share it anyway:



What about type families instead? I tried to experiment a bit, and I just discovered a limitation which I did not know. Consider the closed type family



type family Output a b where
Output (a->b) a = b
Output a (a->b) = b


The code above compiles, but then the type Output a (a->b) is stuck. The second equation does not get applied, as if the first one could potentially match.



Usually, I can understand this in some other scenarios, but here unifying



Output (a' -> b') b' ~ Output a (a -> b)


seems to fail since we would need a ~ (a' -> b') ~ (a' -> a -> b) which is impossible, with finite types. For some reason, GHC does not use this argument (does it pretend infinite types exist in this check? why?)



Anyway, this makes replacing fundeps with type families harder than it could be, it seems. I have no idea about why GHC accepts the fundeps code I posted, yet refuses the OP's code which is essentially the same thing, except using type families.






share|improve this answer





















  • Reliably excluding infinite types turns out to be rather tricky in the presence of type families. type family Oops where Oops = () ': Oops.
    – dfeuer
    Nov 18 at 15:06










  • @dfeuer True, but in the unification problem above I think we have a'->b' ~ a , b'~a -> b which involves no type families. I guess it was simpler to simply turn off the occurs check in all the cases, rather than only when type families are still involved.
    – chi
    Nov 18 at 15:12












  • @chi Is b' ~ a'; b ~ a' not a valid solution?
    – HTNW
    Nov 18 at 15:26










  • Thank you for your quick reply, chi! My application doesn't involve polymorphic functions so this problem won't arise, although I recognize that this is anissue in the long run. I tried type families as well and run in the same problem you describe. I could solve it by adding a constraint like (Output (a->b) a ~ b), but this leads to some unwanted code duplication and, I suspect, more problems down the road.
    – Ahmad B
    Nov 18 at 16:25










  • @HTNW No, since with your substitution the first constraint becomes (a'->a') ~ a which is not generally true.
    – chi
    Nov 18 at 16:43


















up vote
1
down vote













@chi is close; an approach using either FunDeps or Closed Type Families is possible. But the Combine instances are potentially ambiguous/unifiable just as much as the CTF Output equations.



When chi says the FunDep code is accepted, that's only half-true: GHC plain leads you down the garden path. It will accept the instances but then you find you can't use them/you get weird error messages. See the Users Guide at "potential for overlap".



If you're looking to resolve a potentially ambiguous Combine constraint, you might get an error suggesting you try IncoherentInstances (or INCOHERENT pragma). Don't do that. You have a genuinely incoherent problem; all that will do is defer the problem to somewhere else. It's always possible to avoid Incoherent -- providing you can rejig your instances (as follows) and they're not locked away in libraries.



Notice that because of the potential ambiguity, another Haskell compiler (Hugs) doesn't let you write Combine like that. It has a more correct implementation of Haskell's (not-well-stated) rules.



The answer is to use a sort of overlap where one instance is strictly more specific. You must first decide which you way you want to prefer in case of ambiguity. I'll choose function prefixed to argument:



{-# LANGUAGE UndecidableInstances, TypeFamilies #-}

instance {-# OVERLAPPING #-} (r ~ b)
=> Combine (a->b) a r where ...

instance {-# OVERLAPPABLE #-} (Combine2 t1 t2 r)
=> Combine t1 t2 r where
(<^>) = revApp

class Combine2 t1 t2 r | t1 t2 -> r where
revApp :: t1 -> t2 -> r

instance (b ~ r) => Combine2 a (a->b) r where
revApp x f = f x


Notice that the OVERLAPPABLE instance for Combine has bare tyvars, it's a catch-all so it's always matchable. All the compiler has to do is decide whether some wanted constraint is of the form of the OVERLAPPING instance.



The Combine2 constraint on the OVERLAPPABLE instance is no smaller than the head, so you need UndecidableInstances. Also beware that deferring to Combine2 will mean that if the compiler still can't resolve, you're likely to get puzzling error messages.



Talking of bare tyvars/"always matchable", I've used an additional trick to make the compiler work really hard to improve the types: There's bare r in the head of the instance, with an Equality type improvement constraint (b ~ r) =>. To use the ~, you need to switch on TypeFamilies even though you're not writing any type families.



A CTF approach would be similar. You need a catch-all equation on Output that calls an auxiliary type function. Again you need UndecidableInstances.






share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53361478%2foverlapping-instances-how-to-circumvent-them%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote



    accepted










    This is a bad idea, in my view, but I'll play along.



    A possible solution is to switch to functional dependencies. Usually I tend to avoid fundeps in favor of type families, but here they make the instances compile in a simple way.



    class Combine t1 t2 r | t1 t2 -> r where
    (<^>) :: t1 -> t2 -> r

    instance Combine (a->b) a b where
    f <^> x = f x

    instance Combine a (a->b) b where
    x <^> f = f x


    Note that this class will likely cause problems during type inference if we use polymorphic functions. This is because, with polymorphic functions, the code can easily become ambiguous.



    For instance id <^> id could pick any of the two instances. Above, melpomene already reported const <^> id being ambiguous as well.





    The following is weakly related, but I want to share it anyway:



    What about type families instead? I tried to experiment a bit, and I just discovered a limitation which I did not know. Consider the closed type family



    type family Output a b where
    Output (a->b) a = b
    Output a (a->b) = b


    The code above compiles, but then the type Output a (a->b) is stuck. The second equation does not get applied, as if the first one could potentially match.



    Usually, I can understand this in some other scenarios, but here unifying



    Output (a' -> b') b' ~ Output a (a -> b)


    seems to fail since we would need a ~ (a' -> b') ~ (a' -> a -> b) which is impossible, with finite types. For some reason, GHC does not use this argument (does it pretend infinite types exist in this check? why?)



    Anyway, this makes replacing fundeps with type families harder than it could be, it seems. I have no idea about why GHC accepts the fundeps code I posted, yet refuses the OP's code which is essentially the same thing, except using type families.






    share|improve this answer





















    • Reliably excluding infinite types turns out to be rather tricky in the presence of type families. type family Oops where Oops = () ': Oops.
      – dfeuer
      Nov 18 at 15:06










    • @dfeuer True, but in the unification problem above I think we have a'->b' ~ a , b'~a -> b which involves no type families. I guess it was simpler to simply turn off the occurs check in all the cases, rather than only when type families are still involved.
      – chi
      Nov 18 at 15:12












    • @chi Is b' ~ a'; b ~ a' not a valid solution?
      – HTNW
      Nov 18 at 15:26










    • Thank you for your quick reply, chi! My application doesn't involve polymorphic functions so this problem won't arise, although I recognize that this is anissue in the long run. I tried type families as well and run in the same problem you describe. I could solve it by adding a constraint like (Output (a->b) a ~ b), but this leads to some unwanted code duplication and, I suspect, more problems down the road.
      – Ahmad B
      Nov 18 at 16:25










    • @HTNW No, since with your substitution the first constraint becomes (a'->a') ~ a which is not generally true.
      – chi
      Nov 18 at 16:43















    up vote
    1
    down vote



    accepted










    This is a bad idea, in my view, but I'll play along.



    A possible solution is to switch to functional dependencies. Usually I tend to avoid fundeps in favor of type families, but here they make the instances compile in a simple way.



    class Combine t1 t2 r | t1 t2 -> r where
    (<^>) :: t1 -> t2 -> r

    instance Combine (a->b) a b where
    f <^> x = f x

    instance Combine a (a->b) b where
    x <^> f = f x


    Note that this class will likely cause problems during type inference if we use polymorphic functions. This is because, with polymorphic functions, the code can easily become ambiguous.



    For instance id <^> id could pick any of the two instances. Above, melpomene already reported const <^> id being ambiguous as well.





    The following is weakly related, but I want to share it anyway:



    What about type families instead? I tried to experiment a bit, and I just discovered a limitation which I did not know. Consider the closed type family



    type family Output a b where
    Output (a->b) a = b
    Output a (a->b) = b


    The code above compiles, but then the type Output a (a->b) is stuck. The second equation does not get applied, as if the first one could potentially match.



    Usually, I can understand this in some other scenarios, but here unifying



    Output (a' -> b') b' ~ Output a (a -> b)


    seems to fail since we would need a ~ (a' -> b') ~ (a' -> a -> b) which is impossible, with finite types. For some reason, GHC does not use this argument (does it pretend infinite types exist in this check? why?)



    Anyway, this makes replacing fundeps with type families harder than it could be, it seems. I have no idea about why GHC accepts the fundeps code I posted, yet refuses the OP's code which is essentially the same thing, except using type families.






    share|improve this answer





















    • Reliably excluding infinite types turns out to be rather tricky in the presence of type families. type family Oops where Oops = () ': Oops.
      – dfeuer
      Nov 18 at 15:06










    • @dfeuer True, but in the unification problem above I think we have a'->b' ~ a , b'~a -> b which involves no type families. I guess it was simpler to simply turn off the occurs check in all the cases, rather than only when type families are still involved.
      – chi
      Nov 18 at 15:12












    • @chi Is b' ~ a'; b ~ a' not a valid solution?
      – HTNW
      Nov 18 at 15:26










    • Thank you for your quick reply, chi! My application doesn't involve polymorphic functions so this problem won't arise, although I recognize that this is anissue in the long run. I tried type families as well and run in the same problem you describe. I could solve it by adding a constraint like (Output (a->b) a ~ b), but this leads to some unwanted code duplication and, I suspect, more problems down the road.
      – Ahmad B
      Nov 18 at 16:25










    • @HTNW No, since with your substitution the first constraint becomes (a'->a') ~ a which is not generally true.
      – chi
      Nov 18 at 16:43













    up vote
    1
    down vote



    accepted







    up vote
    1
    down vote



    accepted






    This is a bad idea, in my view, but I'll play along.



    A possible solution is to switch to functional dependencies. Usually I tend to avoid fundeps in favor of type families, but here they make the instances compile in a simple way.



    class Combine t1 t2 r | t1 t2 -> r where
    (<^>) :: t1 -> t2 -> r

    instance Combine (a->b) a b where
    f <^> x = f x

    instance Combine a (a->b) b where
    x <^> f = f x


    Note that this class will likely cause problems during type inference if we use polymorphic functions. This is because, with polymorphic functions, the code can easily become ambiguous.



    For instance id <^> id could pick any of the two instances. Above, melpomene already reported const <^> id being ambiguous as well.





    The following is weakly related, but I want to share it anyway:



    What about type families instead? I tried to experiment a bit, and I just discovered a limitation which I did not know. Consider the closed type family



    type family Output a b where
    Output (a->b) a = b
    Output a (a->b) = b


    The code above compiles, but then the type Output a (a->b) is stuck. The second equation does not get applied, as if the first one could potentially match.



    Usually, I can understand this in some other scenarios, but here unifying



    Output (a' -> b') b' ~ Output a (a -> b)


    seems to fail since we would need a ~ (a' -> b') ~ (a' -> a -> b) which is impossible, with finite types. For some reason, GHC does not use this argument (does it pretend infinite types exist in this check? why?)



    Anyway, this makes replacing fundeps with type families harder than it could be, it seems. I have no idea about why GHC accepts the fundeps code I posted, yet refuses the OP's code which is essentially the same thing, except using type families.






    share|improve this answer












    This is a bad idea, in my view, but I'll play along.



    A possible solution is to switch to functional dependencies. Usually I tend to avoid fundeps in favor of type families, but here they make the instances compile in a simple way.



    class Combine t1 t2 r | t1 t2 -> r where
    (<^>) :: t1 -> t2 -> r

    instance Combine (a->b) a b where
    f <^> x = f x

    instance Combine a (a->b) b where
    x <^> f = f x


    Note that this class will likely cause problems during type inference if we use polymorphic functions. This is because, with polymorphic functions, the code can easily become ambiguous.



    For instance id <^> id could pick any of the two instances. Above, melpomene already reported const <^> id being ambiguous as well.





    The following is weakly related, but I want to share it anyway:



    What about type families instead? I tried to experiment a bit, and I just discovered a limitation which I did not know. Consider the closed type family



    type family Output a b where
    Output (a->b) a = b
    Output a (a->b) = b


    The code above compiles, but then the type Output a (a->b) is stuck. The second equation does not get applied, as if the first one could potentially match.



    Usually, I can understand this in some other scenarios, but here unifying



    Output (a' -> b') b' ~ Output a (a -> b)


    seems to fail since we would need a ~ (a' -> b') ~ (a' -> a -> b) which is impossible, with finite types. For some reason, GHC does not use this argument (does it pretend infinite types exist in this check? why?)



    Anyway, this makes replacing fundeps with type families harder than it could be, it seems. I have no idea about why GHC accepts the fundeps code I posted, yet refuses the OP's code which is essentially the same thing, except using type families.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 18 at 14:09









    chi

    71.9k279132




    71.9k279132












    • Reliably excluding infinite types turns out to be rather tricky in the presence of type families. type family Oops where Oops = () ': Oops.
      – dfeuer
      Nov 18 at 15:06










    • @dfeuer True, but in the unification problem above I think we have a'->b' ~ a , b'~a -> b which involves no type families. I guess it was simpler to simply turn off the occurs check in all the cases, rather than only when type families are still involved.
      – chi
      Nov 18 at 15:12












    • @chi Is b' ~ a'; b ~ a' not a valid solution?
      – HTNW
      Nov 18 at 15:26










    • Thank you for your quick reply, chi! My application doesn't involve polymorphic functions so this problem won't arise, although I recognize that this is anissue in the long run. I tried type families as well and run in the same problem you describe. I could solve it by adding a constraint like (Output (a->b) a ~ b), but this leads to some unwanted code duplication and, I suspect, more problems down the road.
      – Ahmad B
      Nov 18 at 16:25










    • @HTNW No, since with your substitution the first constraint becomes (a'->a') ~ a which is not generally true.
      – chi
      Nov 18 at 16:43


















    • Reliably excluding infinite types turns out to be rather tricky in the presence of type families. type family Oops where Oops = () ': Oops.
      – dfeuer
      Nov 18 at 15:06










    • @dfeuer True, but in the unification problem above I think we have a'->b' ~ a , b'~a -> b which involves no type families. I guess it was simpler to simply turn off the occurs check in all the cases, rather than only when type families are still involved.
      – chi
      Nov 18 at 15:12












    • @chi Is b' ~ a'; b ~ a' not a valid solution?
      – HTNW
      Nov 18 at 15:26










    • Thank you for your quick reply, chi! My application doesn't involve polymorphic functions so this problem won't arise, although I recognize that this is anissue in the long run. I tried type families as well and run in the same problem you describe. I could solve it by adding a constraint like (Output (a->b) a ~ b), but this leads to some unwanted code duplication and, I suspect, more problems down the road.
      – Ahmad B
      Nov 18 at 16:25










    • @HTNW No, since with your substitution the first constraint becomes (a'->a') ~ a which is not generally true.
      – chi
      Nov 18 at 16:43
















    Reliably excluding infinite types turns out to be rather tricky in the presence of type families. type family Oops where Oops = () ': Oops.
    – dfeuer
    Nov 18 at 15:06




    Reliably excluding infinite types turns out to be rather tricky in the presence of type families. type family Oops where Oops = () ': Oops.
    – dfeuer
    Nov 18 at 15:06












    @dfeuer True, but in the unification problem above I think we have a'->b' ~ a , b'~a -> b which involves no type families. I guess it was simpler to simply turn off the occurs check in all the cases, rather than only when type families are still involved.
    – chi
    Nov 18 at 15:12






    @dfeuer True, but in the unification problem above I think we have a'->b' ~ a , b'~a -> b which involves no type families. I guess it was simpler to simply turn off the occurs check in all the cases, rather than only when type families are still involved.
    – chi
    Nov 18 at 15:12














    @chi Is b' ~ a'; b ~ a' not a valid solution?
    – HTNW
    Nov 18 at 15:26




    @chi Is b' ~ a'; b ~ a' not a valid solution?
    – HTNW
    Nov 18 at 15:26












    Thank you for your quick reply, chi! My application doesn't involve polymorphic functions so this problem won't arise, although I recognize that this is anissue in the long run. I tried type families as well and run in the same problem you describe. I could solve it by adding a constraint like (Output (a->b) a ~ b), but this leads to some unwanted code duplication and, I suspect, more problems down the road.
    – Ahmad B
    Nov 18 at 16:25




    Thank you for your quick reply, chi! My application doesn't involve polymorphic functions so this problem won't arise, although I recognize that this is anissue in the long run. I tried type families as well and run in the same problem you describe. I could solve it by adding a constraint like (Output (a->b) a ~ b), but this leads to some unwanted code duplication and, I suspect, more problems down the road.
    – Ahmad B
    Nov 18 at 16:25












    @HTNW No, since with your substitution the first constraint becomes (a'->a') ~ a which is not generally true.
    – chi
    Nov 18 at 16:43




    @HTNW No, since with your substitution the first constraint becomes (a'->a') ~ a which is not generally true.
    – chi
    Nov 18 at 16:43












    up vote
    1
    down vote













    @chi is close; an approach using either FunDeps or Closed Type Families is possible. But the Combine instances are potentially ambiguous/unifiable just as much as the CTF Output equations.



    When chi says the FunDep code is accepted, that's only half-true: GHC plain leads you down the garden path. It will accept the instances but then you find you can't use them/you get weird error messages. See the Users Guide at "potential for overlap".



    If you're looking to resolve a potentially ambiguous Combine constraint, you might get an error suggesting you try IncoherentInstances (or INCOHERENT pragma). Don't do that. You have a genuinely incoherent problem; all that will do is defer the problem to somewhere else. It's always possible to avoid Incoherent -- providing you can rejig your instances (as follows) and they're not locked away in libraries.



    Notice that because of the potential ambiguity, another Haskell compiler (Hugs) doesn't let you write Combine like that. It has a more correct implementation of Haskell's (not-well-stated) rules.



    The answer is to use a sort of overlap where one instance is strictly more specific. You must first decide which you way you want to prefer in case of ambiguity. I'll choose function prefixed to argument:



    {-# LANGUAGE UndecidableInstances, TypeFamilies #-}

    instance {-# OVERLAPPING #-} (r ~ b)
    => Combine (a->b) a r where ...

    instance {-# OVERLAPPABLE #-} (Combine2 t1 t2 r)
    => Combine t1 t2 r where
    (<^>) = revApp

    class Combine2 t1 t2 r | t1 t2 -> r where
    revApp :: t1 -> t2 -> r

    instance (b ~ r) => Combine2 a (a->b) r where
    revApp x f = f x


    Notice that the OVERLAPPABLE instance for Combine has bare tyvars, it's a catch-all so it's always matchable. All the compiler has to do is decide whether some wanted constraint is of the form of the OVERLAPPING instance.



    The Combine2 constraint on the OVERLAPPABLE instance is no smaller than the head, so you need UndecidableInstances. Also beware that deferring to Combine2 will mean that if the compiler still can't resolve, you're likely to get puzzling error messages.



    Talking of bare tyvars/"always matchable", I've used an additional trick to make the compiler work really hard to improve the types: There's bare r in the head of the instance, with an Equality type improvement constraint (b ~ r) =>. To use the ~, you need to switch on TypeFamilies even though you're not writing any type families.



    A CTF approach would be similar. You need a catch-all equation on Output that calls an auxiliary type function. Again you need UndecidableInstances.






    share|improve this answer

























      up vote
      1
      down vote













      @chi is close; an approach using either FunDeps or Closed Type Families is possible. But the Combine instances are potentially ambiguous/unifiable just as much as the CTF Output equations.



      When chi says the FunDep code is accepted, that's only half-true: GHC plain leads you down the garden path. It will accept the instances but then you find you can't use them/you get weird error messages. See the Users Guide at "potential for overlap".



      If you're looking to resolve a potentially ambiguous Combine constraint, you might get an error suggesting you try IncoherentInstances (or INCOHERENT pragma). Don't do that. You have a genuinely incoherent problem; all that will do is defer the problem to somewhere else. It's always possible to avoid Incoherent -- providing you can rejig your instances (as follows) and they're not locked away in libraries.



      Notice that because of the potential ambiguity, another Haskell compiler (Hugs) doesn't let you write Combine like that. It has a more correct implementation of Haskell's (not-well-stated) rules.



      The answer is to use a sort of overlap where one instance is strictly more specific. You must first decide which you way you want to prefer in case of ambiguity. I'll choose function prefixed to argument:



      {-# LANGUAGE UndecidableInstances, TypeFamilies #-}

      instance {-# OVERLAPPING #-} (r ~ b)
      => Combine (a->b) a r where ...

      instance {-# OVERLAPPABLE #-} (Combine2 t1 t2 r)
      => Combine t1 t2 r where
      (<^>) = revApp

      class Combine2 t1 t2 r | t1 t2 -> r where
      revApp :: t1 -> t2 -> r

      instance (b ~ r) => Combine2 a (a->b) r where
      revApp x f = f x


      Notice that the OVERLAPPABLE instance for Combine has bare tyvars, it's a catch-all so it's always matchable. All the compiler has to do is decide whether some wanted constraint is of the form of the OVERLAPPING instance.



      The Combine2 constraint on the OVERLAPPABLE instance is no smaller than the head, so you need UndecidableInstances. Also beware that deferring to Combine2 will mean that if the compiler still can't resolve, you're likely to get puzzling error messages.



      Talking of bare tyvars/"always matchable", I've used an additional trick to make the compiler work really hard to improve the types: There's bare r in the head of the instance, with an Equality type improvement constraint (b ~ r) =>. To use the ~, you need to switch on TypeFamilies even though you're not writing any type families.



      A CTF approach would be similar. You need a catch-all equation on Output that calls an auxiliary type function. Again you need UndecidableInstances.






      share|improve this answer























        up vote
        1
        down vote










        up vote
        1
        down vote









        @chi is close; an approach using either FunDeps or Closed Type Families is possible. But the Combine instances are potentially ambiguous/unifiable just as much as the CTF Output equations.



        When chi says the FunDep code is accepted, that's only half-true: GHC plain leads you down the garden path. It will accept the instances but then you find you can't use them/you get weird error messages. See the Users Guide at "potential for overlap".



        If you're looking to resolve a potentially ambiguous Combine constraint, you might get an error suggesting you try IncoherentInstances (or INCOHERENT pragma). Don't do that. You have a genuinely incoherent problem; all that will do is defer the problem to somewhere else. It's always possible to avoid Incoherent -- providing you can rejig your instances (as follows) and they're not locked away in libraries.



        Notice that because of the potential ambiguity, another Haskell compiler (Hugs) doesn't let you write Combine like that. It has a more correct implementation of Haskell's (not-well-stated) rules.



        The answer is to use a sort of overlap where one instance is strictly more specific. You must first decide which you way you want to prefer in case of ambiguity. I'll choose function prefixed to argument:



        {-# LANGUAGE UndecidableInstances, TypeFamilies #-}

        instance {-# OVERLAPPING #-} (r ~ b)
        => Combine (a->b) a r where ...

        instance {-# OVERLAPPABLE #-} (Combine2 t1 t2 r)
        => Combine t1 t2 r where
        (<^>) = revApp

        class Combine2 t1 t2 r | t1 t2 -> r where
        revApp :: t1 -> t2 -> r

        instance (b ~ r) => Combine2 a (a->b) r where
        revApp x f = f x


        Notice that the OVERLAPPABLE instance for Combine has bare tyvars, it's a catch-all so it's always matchable. All the compiler has to do is decide whether some wanted constraint is of the form of the OVERLAPPING instance.



        The Combine2 constraint on the OVERLAPPABLE instance is no smaller than the head, so you need UndecidableInstances. Also beware that deferring to Combine2 will mean that if the compiler still can't resolve, you're likely to get puzzling error messages.



        Talking of bare tyvars/"always matchable", I've used an additional trick to make the compiler work really hard to improve the types: There's bare r in the head of the instance, with an Equality type improvement constraint (b ~ r) =>. To use the ~, you need to switch on TypeFamilies even though you're not writing any type families.



        A CTF approach would be similar. You need a catch-all equation on Output that calls an auxiliary type function. Again you need UndecidableInstances.






        share|improve this answer












        @chi is close; an approach using either FunDeps or Closed Type Families is possible. But the Combine instances are potentially ambiguous/unifiable just as much as the CTF Output equations.



        When chi says the FunDep code is accepted, that's only half-true: GHC plain leads you down the garden path. It will accept the instances but then you find you can't use them/you get weird error messages. See the Users Guide at "potential for overlap".



        If you're looking to resolve a potentially ambiguous Combine constraint, you might get an error suggesting you try IncoherentInstances (or INCOHERENT pragma). Don't do that. You have a genuinely incoherent problem; all that will do is defer the problem to somewhere else. It's always possible to avoid Incoherent -- providing you can rejig your instances (as follows) and they're not locked away in libraries.



        Notice that because of the potential ambiguity, another Haskell compiler (Hugs) doesn't let you write Combine like that. It has a more correct implementation of Haskell's (not-well-stated) rules.



        The answer is to use a sort of overlap where one instance is strictly more specific. You must first decide which you way you want to prefer in case of ambiguity. I'll choose function prefixed to argument:



        {-# LANGUAGE UndecidableInstances, TypeFamilies #-}

        instance {-# OVERLAPPING #-} (r ~ b)
        => Combine (a->b) a r where ...

        instance {-# OVERLAPPABLE #-} (Combine2 t1 t2 r)
        => Combine t1 t2 r where
        (<^>) = revApp

        class Combine2 t1 t2 r | t1 t2 -> r where
        revApp :: t1 -> t2 -> r

        instance (b ~ r) => Combine2 a (a->b) r where
        revApp x f = f x


        Notice that the OVERLAPPABLE instance for Combine has bare tyvars, it's a catch-all so it's always matchable. All the compiler has to do is decide whether some wanted constraint is of the form of the OVERLAPPING instance.



        The Combine2 constraint on the OVERLAPPABLE instance is no smaller than the head, so you need UndecidableInstances. Also beware that deferring to Combine2 will mean that if the compiler still can't resolve, you're likely to get puzzling error messages.



        Talking of bare tyvars/"always matchable", I've used an additional trick to make the compiler work really hard to improve the types: There's bare r in the head of the instance, with an Equality type improvement constraint (b ~ r) =>. To use the ~, you need to switch on TypeFamilies even though you're not writing any type families.



        A CTF approach would be similar. You need a catch-all equation on Output that calls an auxiliary type function. Again you need UndecidableInstances.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 19 at 9:33









        AntC

        542210




        542210






























             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53361478%2foverlapping-instances-how-to-circumvent-them%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Paul Cézanne

            UIScrollView CustomStickyHeader Resize height generates problems when scroll is too fast

            Angular material date-picker (MatDatepicker) auto completes the date on focus out