Overlapping instances - how to circumvent them












2















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 '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 '18 at 11:11
















2















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 '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 '18 at 11:11














2












2








2


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 '18 at 13:37









Ahmad BAhmad 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 '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 '18 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 '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 '18 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 '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 '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 '18 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 '18 at 11:11












2 Answers
2






active

oldest

votes


















1














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 '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 '18 at 15:12













  • @chi Is b' ~ a'; b ~ a' not a valid solution?

    – HTNW
    Nov 18 '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 '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 '18 at 16:43



















1














@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',
    autoActivateHeartbeat: false,
    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









    1














    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 '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 '18 at 15:12













    • @chi Is b' ~ a'; b ~ a' not a valid solution?

      – HTNW
      Nov 18 '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 '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 '18 at 16:43
















    1














    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 '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 '18 at 15:12













    • @chi Is b' ~ a'; b ~ a' not a valid solution?

      – HTNW
      Nov 18 '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 '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 '18 at 16:43














    1












    1








    1







    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 '18 at 14:09









    chichi

    74.3k284140




    74.3k284140













    • 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 '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 '18 at 15:12













    • @chi Is b' ~ a'; b ~ a' not a valid solution?

      – HTNW
      Nov 18 '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 '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 '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 '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 '18 at 15:12













    • @chi Is b' ~ a'; b ~ a' not a valid solution?

      – HTNW
      Nov 18 '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 '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 '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 '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 '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 '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 '18 at 15:12















    @chi Is b' ~ a'; b ~ a' not a valid solution?

    – HTNW
    Nov 18 '18 at 15:26





    @chi Is b' ~ a'; b ~ a' not a valid solution?

    – HTNW
    Nov 18 '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 '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 '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 '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 '18 at 16:43













    1














    @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




























      1














      @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


























        1












        1








        1







        @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 '18 at 9:33









        AntCAntC

        587210




        587210






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            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







            這個網誌中的熱門文章

            Academy of Television Arts & Sciences

            L'Équipe

            1995 France bombings