Bidirectional Functional DependenciesLarge-scale design in Haskell?Functional Dependency in HaskellSpeed comparison with Project Euler: C vs Python vs Erlang vs HaskellHaskell functional dependency conflictAbusing the algebra of algebraic data types - why does this work?GHC code generation for type class function callsWhy not be dependently typed?Confused by the meaning of the 'Alternative' type class and its relationship to other type classesMultiParamTypeClasses, FunctionalDependencies, and calling ambiguous functionsWhy does my functional dependency conflict disappear when I expand the definition?

What is this airplane that sits in front of Barringer High School in Newark, NJ?

Print the new site header

In Street Fighter, what does the M stand for in M Bison?

How to best clean this sealed rotary encoder / volume knob?

How can a clan of females defend themselves in the ancient world against wandering bands?

Are intrusions within a foreign embassy considered an act of war?

In a list with unique pairs A, B, how can I sort them so that the last B is the first A in the next pair?

How much steel armor can you wear and still be able to swim?

"Correct me if I'm wrong"

How to modify a string without altering its text properties

How can I restore a master database from its bak file?

How "fast" do astronomical events occur?

Why are there no file insertion syscalls

"Prove that ∂A is closed given ∂A = Cl(A) − Int(A)"

No shading in ContourPlot3D

How is the idea of "girlfriend material" naturally expressed in Russian?

What does it cost to buy a tavern?

What preparations would Hubble have needed to return in a Shuttle?

Is there any possible way to get these hearts as Adult Link?

Is Newton's third law really correct?

How can a warlock learn from a spellbook?

Summing cube roots in fractions

Teferi's Time Twist and Gideon's Sacrifice

What mathematical theory is required for high frequency trading?



Bidirectional Functional Dependencies


Large-scale design in Haskell?Functional Dependency in HaskellSpeed comparison with Project Euler: C vs Python vs Erlang vs HaskellHaskell functional dependency conflictAbusing the algebra of algebraic data types - why does this work?GHC code generation for type class function callsWhy not be dependently typed?Confused by the meaning of the 'Alternative' type class and its relationship to other type classesMultiParamTypeClasses, FunctionalDependencies, and calling ambiguous functionsWhy does my functional dependency conflict disappear when I expand the definition?






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;








8















I have a type class that looks a bit like the following:



class Foo a b | a -> b where
f :: a -> Bool
g :: b -> Bool
h :: a -> b -> Bool


Or at least these are the bits that are important to my question. This class does not compile, and for good reason. The problem with this class is that I could (if I wanted to) do the following:



instance Foo () Bool where
f x = True
g y = y
h x y = False

instance Foo ((), ()) Bool where
f x = True
g y = not y
h x y = False


Now if I call g True there are two separate results one for each instance. And the compiler picks up on this possibility and informs me that my type class is no good.



My issue is that the dependency | a -> b is not quite what I mean. I don't just mean that you can find a from b, but also that you can find b from a. That is each type should only ever be a member of Foo with one other type so we can given one type find the other. Or to put it in yet another way the dependency is bidirectional. Such a functional dependency would prevent me from having Bool present in two separate instances because the first parameter would be derivable from the second as well as the second from the first.



But I don't know how to express this idea to the compiler.



How can I create a bidirectional functional dependency? Or, more likely, is there a way that I can rephrase my type class to get something that could replace a bidirectional functional dependency?










share|improve this question
























  • You can list multiple ones like class Foo a b | a -> b, b -> a where ..., but that would make the two instance Foo ... Bools problematic.

    – Willem Van Onsem
    Jun 1 at 21:33

















8















I have a type class that looks a bit like the following:



class Foo a b | a -> b where
f :: a -> Bool
g :: b -> Bool
h :: a -> b -> Bool


Or at least these are the bits that are important to my question. This class does not compile, and for good reason. The problem with this class is that I could (if I wanted to) do the following:



instance Foo () Bool where
f x = True
g y = y
h x y = False

instance Foo ((), ()) Bool where
f x = True
g y = not y
h x y = False


Now if I call g True there are two separate results one for each instance. And the compiler picks up on this possibility and informs me that my type class is no good.



My issue is that the dependency | a -> b is not quite what I mean. I don't just mean that you can find a from b, but also that you can find b from a. That is each type should only ever be a member of Foo with one other type so we can given one type find the other. Or to put it in yet another way the dependency is bidirectional. Such a functional dependency would prevent me from having Bool present in two separate instances because the first parameter would be derivable from the second as well as the second from the first.



But I don't know how to express this idea to the compiler.



How can I create a bidirectional functional dependency? Or, more likely, is there a way that I can rephrase my type class to get something that could replace a bidirectional functional dependency?










share|improve this question
























  • You can list multiple ones like class Foo a b | a -> b, b -> a where ..., but that would make the two instance Foo ... Bools problematic.

    – Willem Van Onsem
    Jun 1 at 21:33













8












8








8








I have a type class that looks a bit like the following:



class Foo a b | a -> b where
f :: a -> Bool
g :: b -> Bool
h :: a -> b -> Bool


Or at least these are the bits that are important to my question. This class does not compile, and for good reason. The problem with this class is that I could (if I wanted to) do the following:



instance Foo () Bool where
f x = True
g y = y
h x y = False

instance Foo ((), ()) Bool where
f x = True
g y = not y
h x y = False


Now if I call g True there are two separate results one for each instance. And the compiler picks up on this possibility and informs me that my type class is no good.



My issue is that the dependency | a -> b is not quite what I mean. I don't just mean that you can find a from b, but also that you can find b from a. That is each type should only ever be a member of Foo with one other type so we can given one type find the other. Or to put it in yet another way the dependency is bidirectional. Such a functional dependency would prevent me from having Bool present in two separate instances because the first parameter would be derivable from the second as well as the second from the first.



But I don't know how to express this idea to the compiler.



How can I create a bidirectional functional dependency? Or, more likely, is there a way that I can rephrase my type class to get something that could replace a bidirectional functional dependency?










share|improve this question
















I have a type class that looks a bit like the following:



class Foo a b | a -> b where
f :: a -> Bool
g :: b -> Bool
h :: a -> b -> Bool


Or at least these are the bits that are important to my question. This class does not compile, and for good reason. The problem with this class is that I could (if I wanted to) do the following:



instance Foo () Bool where
f x = True
g y = y
h x y = False

instance Foo ((), ()) Bool where
f x = True
g y = not y
h x y = False


Now if I call g True there are two separate results one for each instance. And the compiler picks up on this possibility and informs me that my type class is no good.



My issue is that the dependency | a -> b is not quite what I mean. I don't just mean that you can find a from b, but also that you can find b from a. That is each type should only ever be a member of Foo with one other type so we can given one type find the other. Or to put it in yet another way the dependency is bidirectional. Such a functional dependency would prevent me from having Bool present in two separate instances because the first parameter would be derivable from the second as well as the second from the first.



But I don't know how to express this idea to the compiler.



How can I create a bidirectional functional dependency? Or, more likely, is there a way that I can rephrase my type class to get something that could replace a bidirectional functional dependency?







haskell typeclass functional-dependencies type-level-computation






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jun 2 at 2:12







Sriotchilism O'Zaic

















asked Jun 1 at 21:24









Sriotchilism O'ZaicSriotchilism O'Zaic

910620




910620












  • You can list multiple ones like class Foo a b | a -> b, b -> a where ..., but that would make the two instance Foo ... Bools problematic.

    – Willem Van Onsem
    Jun 1 at 21:33

















  • You can list multiple ones like class Foo a b | a -> b, b -> a where ..., but that would make the two instance Foo ... Bools problematic.

    – Willem Van Onsem
    Jun 1 at 21:33
















You can list multiple ones like class Foo a b | a -> b, b -> a where ..., but that would make the two instance Foo ... Bools problematic.

– Willem Van Onsem
Jun 1 at 21:33





You can list multiple ones like class Foo a b | a -> b, b -> a where ..., but that would make the two instance Foo ... Bools problematic.

– Willem Van Onsem
Jun 1 at 21:33












3 Answers
3






active

oldest

votes


















8














A bidirectional dependency between a and b can be presented as two functional dependencies a -> b and b -> a, like:



class Foo a b | a -> b, b -> a where
f :: a -> Bool
g :: b -> Bool
h :: a -> b -> Bool


So here a is functional dependent on b and b is functional dependent on a.



For your instances however this of course raises an error, since now you defined two different as for b ~ Bool. This will raise an error like:



file.hs:6:10: error:
Functional dependencies conflict between instance declarations:
instance Foo () Bool -- Defined at file.hs:6:10
instance Foo ((), ()) Bool -- Defined at file.hs:11:10
Failed, modules loaded: none.


Because of the functional dependency, you can only define one a for b ~ Bool. But this is probably exactly what you are looking for: a mechanism to prevent defining Foo twice for the same a, or the same b.






share|improve this answer




















  • 1





    Thank you. That is exactly what I am looking for, the code I was showing was specifically code that I did not want to be possible, that was possible with the single functional dependency, perhaps I should have worded it better.

    – Sriotchilism O'Zaic
    Jun 2 at 2:06


















3














(This is more a comment than an answer, since it does not address the exact question the OP asked.)



To complement Willem's answer: nowadays we have another way to make GHC accept this class.



class Foo a b | a -> b where
f :: a -> Bool
g :: b -> Bool
h :: a -> b -> Bool


As GHC suggests in its error message, we can turn on AllowAmbiguousTypes. The OP noted that then run in troubles if we evaluate something like g False and there are two matching instances like



instance Foo () Bool where
f x = True
g y = y
h x y = False

instance Foo ((), ()) Bool where
f x = True
g y = not y
h x y = False


Indeed, in such case g False becomes ambiguous. We then have two options.



First, we can forbid having both the instances above by adding a functional dependency b -> a to the class (as Willem suggested). That makes g False to be unambiguous (and we do not need the extension in such case).



Alternatively, we can leave both instances in the code, and disambiguate the call g False using type applications (another extension). For instance, g @() False chooses the first instance, while g @((),()) False chooses the second one.



Full code:



-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies,
FlexibleInstances, AllowAmbiguousTypes, TypeApplications #-

class Foo a b | a -> b where
f :: a -> Bool
g :: b -> Bool
h :: a -> b -> Bool

instance Foo () Bool where
f x = True
g y = y
h x y = False

instance Foo ((), ()) Bool where
f x = True
g y = not y
h x y = False

main :: IO ()
main = print (g @() False, g @((),()) False)





share|improve this answer






























    0














    Willem Van Onsem's answer is pretty much exactly what I wanted, but there is another way I realized that might be worth mentioning. To get the intended behavior we can actually split our class up into multiple classes. There are a couple of ways to do this and the best option probably depends on the specifics. But here is one way you could do it for the code from the question:



    class Bar b where
    g :: b -> Bool

    class (Bar b) => Foo a b | a -> b where
    f :: a -> Bool
    h :: a -> b -> Bool


    Now we do still allow us to make two different Foo instances with the same b, but we no longer get the ambiguity since g is now a member of Bar there must be a single instance across the two.



    This can be done in general by moving the functions that might be ambiguous and moving it to a separate type class.



    Another way we can use additional type classes to create a second class to enforce the bidirectionality. For the example this would look like:



    class Bar a b | b -> a

    class (Bar a b) => Foo a b | a -> b where
    f :: a -> Bool
    g :: b -> Bool
    h :: a -> b -> Bool


    Here Bar is acts to make b dependent on a preventing us from having the ambiguity. Since Foo requires Bar and Bar allows a to be derived from b, any instance of Foo allows a to be derived from b. This is pretty much what I wanted originally, however it is just a slightly more complex version of Willem Van Onsem's answer.






    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%2f56410544%2fbidirectional-functional-dependencies%23new-answer', 'question_page');

      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      8














      A bidirectional dependency between a and b can be presented as two functional dependencies a -> b and b -> a, like:



      class Foo a b | a -> b, b -> a where
      f :: a -> Bool
      g :: b -> Bool
      h :: a -> b -> Bool


      So here a is functional dependent on b and b is functional dependent on a.



      For your instances however this of course raises an error, since now you defined two different as for b ~ Bool. This will raise an error like:



      file.hs:6:10: error:
      Functional dependencies conflict between instance declarations:
      instance Foo () Bool -- Defined at file.hs:6:10
      instance Foo ((), ()) Bool -- Defined at file.hs:11:10
      Failed, modules loaded: none.


      Because of the functional dependency, you can only define one a for b ~ Bool. But this is probably exactly what you are looking for: a mechanism to prevent defining Foo twice for the same a, or the same b.






      share|improve this answer




















      • 1





        Thank you. That is exactly what I am looking for, the code I was showing was specifically code that I did not want to be possible, that was possible with the single functional dependency, perhaps I should have worded it better.

        – Sriotchilism O'Zaic
        Jun 2 at 2:06















      8














      A bidirectional dependency between a and b can be presented as two functional dependencies a -> b and b -> a, like:



      class Foo a b | a -> b, b -> a where
      f :: a -> Bool
      g :: b -> Bool
      h :: a -> b -> Bool


      So here a is functional dependent on b and b is functional dependent on a.



      For your instances however this of course raises an error, since now you defined two different as for b ~ Bool. This will raise an error like:



      file.hs:6:10: error:
      Functional dependencies conflict between instance declarations:
      instance Foo () Bool -- Defined at file.hs:6:10
      instance Foo ((), ()) Bool -- Defined at file.hs:11:10
      Failed, modules loaded: none.


      Because of the functional dependency, you can only define one a for b ~ Bool. But this is probably exactly what you are looking for: a mechanism to prevent defining Foo twice for the same a, or the same b.






      share|improve this answer




















      • 1





        Thank you. That is exactly what I am looking for, the code I was showing was specifically code that I did not want to be possible, that was possible with the single functional dependency, perhaps I should have worded it better.

        – Sriotchilism O'Zaic
        Jun 2 at 2:06













      8












      8








      8







      A bidirectional dependency between a and b can be presented as two functional dependencies a -> b and b -> a, like:



      class Foo a b | a -> b, b -> a where
      f :: a -> Bool
      g :: b -> Bool
      h :: a -> b -> Bool


      So here a is functional dependent on b and b is functional dependent on a.



      For your instances however this of course raises an error, since now you defined two different as for b ~ Bool. This will raise an error like:



      file.hs:6:10: error:
      Functional dependencies conflict between instance declarations:
      instance Foo () Bool -- Defined at file.hs:6:10
      instance Foo ((), ()) Bool -- Defined at file.hs:11:10
      Failed, modules loaded: none.


      Because of the functional dependency, you can only define one a for b ~ Bool. But this is probably exactly what you are looking for: a mechanism to prevent defining Foo twice for the same a, or the same b.






      share|improve this answer















      A bidirectional dependency between a and b can be presented as two functional dependencies a -> b and b -> a, like:



      class Foo a b | a -> b, b -> a where
      f :: a -> Bool
      g :: b -> Bool
      h :: a -> b -> Bool


      So here a is functional dependent on b and b is functional dependent on a.



      For your instances however this of course raises an error, since now you defined two different as for b ~ Bool. This will raise an error like:



      file.hs:6:10: error:
      Functional dependencies conflict between instance declarations:
      instance Foo () Bool -- Defined at file.hs:6:10
      instance Foo ((), ()) Bool -- Defined at file.hs:11:10
      Failed, modules loaded: none.


      Because of the functional dependency, you can only define one a for b ~ Bool. But this is probably exactly what you are looking for: a mechanism to prevent defining Foo twice for the same a, or the same b.







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Jun 1 at 21:41

























      answered Jun 1 at 21:37









      Willem Van OnsemWillem Van Onsem

      163k17165254




      163k17165254







      • 1





        Thank you. That is exactly what I am looking for, the code I was showing was specifically code that I did not want to be possible, that was possible with the single functional dependency, perhaps I should have worded it better.

        – Sriotchilism O'Zaic
        Jun 2 at 2:06












      • 1





        Thank you. That is exactly what I am looking for, the code I was showing was specifically code that I did not want to be possible, that was possible with the single functional dependency, perhaps I should have worded it better.

        – Sriotchilism O'Zaic
        Jun 2 at 2:06







      1




      1





      Thank you. That is exactly what I am looking for, the code I was showing was specifically code that I did not want to be possible, that was possible with the single functional dependency, perhaps I should have worded it better.

      – Sriotchilism O'Zaic
      Jun 2 at 2:06





      Thank you. That is exactly what I am looking for, the code I was showing was specifically code that I did not want to be possible, that was possible with the single functional dependency, perhaps I should have worded it better.

      – Sriotchilism O'Zaic
      Jun 2 at 2:06













      3














      (This is more a comment than an answer, since it does not address the exact question the OP asked.)



      To complement Willem's answer: nowadays we have another way to make GHC accept this class.



      class Foo a b | a -> b where
      f :: a -> Bool
      g :: b -> Bool
      h :: a -> b -> Bool


      As GHC suggests in its error message, we can turn on AllowAmbiguousTypes. The OP noted that then run in troubles if we evaluate something like g False and there are two matching instances like



      instance Foo () Bool where
      f x = True
      g y = y
      h x y = False

      instance Foo ((), ()) Bool where
      f x = True
      g y = not y
      h x y = False


      Indeed, in such case g False becomes ambiguous. We then have two options.



      First, we can forbid having both the instances above by adding a functional dependency b -> a to the class (as Willem suggested). That makes g False to be unambiguous (and we do not need the extension in such case).



      Alternatively, we can leave both instances in the code, and disambiguate the call g False using type applications (another extension). For instance, g @() False chooses the first instance, while g @((),()) False chooses the second one.



      Full code:



      -# LANGUAGE MultiParamTypeClasses, FunctionalDependencies,
      FlexibleInstances, AllowAmbiguousTypes, TypeApplications #-

      class Foo a b | a -> b where
      f :: a -> Bool
      g :: b -> Bool
      h :: a -> b -> Bool

      instance Foo () Bool where
      f x = True
      g y = y
      h x y = False

      instance Foo ((), ()) Bool where
      f x = True
      g y = not y
      h x y = False

      main :: IO ()
      main = print (g @() False, g @((),()) False)





      share|improve this answer



























        3














        (This is more a comment than an answer, since it does not address the exact question the OP asked.)



        To complement Willem's answer: nowadays we have another way to make GHC accept this class.



        class Foo a b | a -> b where
        f :: a -> Bool
        g :: b -> Bool
        h :: a -> b -> Bool


        As GHC suggests in its error message, we can turn on AllowAmbiguousTypes. The OP noted that then run in troubles if we evaluate something like g False and there are two matching instances like



        instance Foo () Bool where
        f x = True
        g y = y
        h x y = False

        instance Foo ((), ()) Bool where
        f x = True
        g y = not y
        h x y = False


        Indeed, in such case g False becomes ambiguous. We then have two options.



        First, we can forbid having both the instances above by adding a functional dependency b -> a to the class (as Willem suggested). That makes g False to be unambiguous (and we do not need the extension in such case).



        Alternatively, we can leave both instances in the code, and disambiguate the call g False using type applications (another extension). For instance, g @() False chooses the first instance, while g @((),()) False chooses the second one.



        Full code:



        -# LANGUAGE MultiParamTypeClasses, FunctionalDependencies,
        FlexibleInstances, AllowAmbiguousTypes, TypeApplications #-

        class Foo a b | a -> b where
        f :: a -> Bool
        g :: b -> Bool
        h :: a -> b -> Bool

        instance Foo () Bool where
        f x = True
        g y = y
        h x y = False

        instance Foo ((), ()) Bool where
        f x = True
        g y = not y
        h x y = False

        main :: IO ()
        main = print (g @() False, g @((),()) False)





        share|improve this answer

























          3












          3








          3







          (This is more a comment than an answer, since it does not address the exact question the OP asked.)



          To complement Willem's answer: nowadays we have another way to make GHC accept this class.



          class Foo a b | a -> b where
          f :: a -> Bool
          g :: b -> Bool
          h :: a -> b -> Bool


          As GHC suggests in its error message, we can turn on AllowAmbiguousTypes. The OP noted that then run in troubles if we evaluate something like g False and there are two matching instances like



          instance Foo () Bool where
          f x = True
          g y = y
          h x y = False

          instance Foo ((), ()) Bool where
          f x = True
          g y = not y
          h x y = False


          Indeed, in such case g False becomes ambiguous. We then have two options.



          First, we can forbid having both the instances above by adding a functional dependency b -> a to the class (as Willem suggested). That makes g False to be unambiguous (and we do not need the extension in such case).



          Alternatively, we can leave both instances in the code, and disambiguate the call g False using type applications (another extension). For instance, g @() False chooses the first instance, while g @((),()) False chooses the second one.



          Full code:



          -# LANGUAGE MultiParamTypeClasses, FunctionalDependencies,
          FlexibleInstances, AllowAmbiguousTypes, TypeApplications #-

          class Foo a b | a -> b where
          f :: a -> Bool
          g :: b -> Bool
          h :: a -> b -> Bool

          instance Foo () Bool where
          f x = True
          g y = y
          h x y = False

          instance Foo ((), ()) Bool where
          f x = True
          g y = not y
          h x y = False

          main :: IO ()
          main = print (g @() False, g @((),()) False)





          share|improve this answer













          (This is more a comment than an answer, since it does not address the exact question the OP asked.)



          To complement Willem's answer: nowadays we have another way to make GHC accept this class.



          class Foo a b | a -> b where
          f :: a -> Bool
          g :: b -> Bool
          h :: a -> b -> Bool


          As GHC suggests in its error message, we can turn on AllowAmbiguousTypes. The OP noted that then run in troubles if we evaluate something like g False and there are two matching instances like



          instance Foo () Bool where
          f x = True
          g y = y
          h x y = False

          instance Foo ((), ()) Bool where
          f x = True
          g y = not y
          h x y = False


          Indeed, in such case g False becomes ambiguous. We then have two options.



          First, we can forbid having both the instances above by adding a functional dependency b -> a to the class (as Willem suggested). That makes g False to be unambiguous (and we do not need the extension in such case).



          Alternatively, we can leave both instances in the code, and disambiguate the call g False using type applications (another extension). For instance, g @() False chooses the first instance, while g @((),()) False chooses the second one.



          Full code:



          -# LANGUAGE MultiParamTypeClasses, FunctionalDependencies,
          FlexibleInstances, AllowAmbiguousTypes, TypeApplications #-

          class Foo a b | a -> b where
          f :: a -> Bool
          g :: b -> Bool
          h :: a -> b -> Bool

          instance Foo () Bool where
          f x = True
          g y = y
          h x y = False

          instance Foo ((), ()) Bool where
          f x = True
          g y = not y
          h x y = False

          main :: IO ()
          main = print (g @() False, g @((),()) False)






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Jun 2 at 8:26









          chichi

          79.6k288151




          79.6k288151





















              0














              Willem Van Onsem's answer is pretty much exactly what I wanted, but there is another way I realized that might be worth mentioning. To get the intended behavior we can actually split our class up into multiple classes. There are a couple of ways to do this and the best option probably depends on the specifics. But here is one way you could do it for the code from the question:



              class Bar b where
              g :: b -> Bool

              class (Bar b) => Foo a b | a -> b where
              f :: a -> Bool
              h :: a -> b -> Bool


              Now we do still allow us to make two different Foo instances with the same b, but we no longer get the ambiguity since g is now a member of Bar there must be a single instance across the two.



              This can be done in general by moving the functions that might be ambiguous and moving it to a separate type class.



              Another way we can use additional type classes to create a second class to enforce the bidirectionality. For the example this would look like:



              class Bar a b | b -> a

              class (Bar a b) => Foo a b | a -> b where
              f :: a -> Bool
              g :: b -> Bool
              h :: a -> b -> Bool


              Here Bar is acts to make b dependent on a preventing us from having the ambiguity. Since Foo requires Bar and Bar allows a to be derived from b, any instance of Foo allows a to be derived from b. This is pretty much what I wanted originally, however it is just a slightly more complex version of Willem Van Onsem's answer.






              share|improve this answer



























                0














                Willem Van Onsem's answer is pretty much exactly what I wanted, but there is another way I realized that might be worth mentioning. To get the intended behavior we can actually split our class up into multiple classes. There are a couple of ways to do this and the best option probably depends on the specifics. But here is one way you could do it for the code from the question:



                class Bar b where
                g :: b -> Bool

                class (Bar b) => Foo a b | a -> b where
                f :: a -> Bool
                h :: a -> b -> Bool


                Now we do still allow us to make two different Foo instances with the same b, but we no longer get the ambiguity since g is now a member of Bar there must be a single instance across the two.



                This can be done in general by moving the functions that might be ambiguous and moving it to a separate type class.



                Another way we can use additional type classes to create a second class to enforce the bidirectionality. For the example this would look like:



                class Bar a b | b -> a

                class (Bar a b) => Foo a b | a -> b where
                f :: a -> Bool
                g :: b -> Bool
                h :: a -> b -> Bool


                Here Bar is acts to make b dependent on a preventing us from having the ambiguity. Since Foo requires Bar and Bar allows a to be derived from b, any instance of Foo allows a to be derived from b. This is pretty much what I wanted originally, however it is just a slightly more complex version of Willem Van Onsem's answer.






                share|improve this answer

























                  0












                  0








                  0







                  Willem Van Onsem's answer is pretty much exactly what I wanted, but there is another way I realized that might be worth mentioning. To get the intended behavior we can actually split our class up into multiple classes. There are a couple of ways to do this and the best option probably depends on the specifics. But here is one way you could do it for the code from the question:



                  class Bar b where
                  g :: b -> Bool

                  class (Bar b) => Foo a b | a -> b where
                  f :: a -> Bool
                  h :: a -> b -> Bool


                  Now we do still allow us to make two different Foo instances with the same b, but we no longer get the ambiguity since g is now a member of Bar there must be a single instance across the two.



                  This can be done in general by moving the functions that might be ambiguous and moving it to a separate type class.



                  Another way we can use additional type classes to create a second class to enforce the bidirectionality. For the example this would look like:



                  class Bar a b | b -> a

                  class (Bar a b) => Foo a b | a -> b where
                  f :: a -> Bool
                  g :: b -> Bool
                  h :: a -> b -> Bool


                  Here Bar is acts to make b dependent on a preventing us from having the ambiguity. Since Foo requires Bar and Bar allows a to be derived from b, any instance of Foo allows a to be derived from b. This is pretty much what I wanted originally, however it is just a slightly more complex version of Willem Van Onsem's answer.






                  share|improve this answer













                  Willem Van Onsem's answer is pretty much exactly what I wanted, but there is another way I realized that might be worth mentioning. To get the intended behavior we can actually split our class up into multiple classes. There are a couple of ways to do this and the best option probably depends on the specifics. But here is one way you could do it for the code from the question:



                  class Bar b where
                  g :: b -> Bool

                  class (Bar b) => Foo a b | a -> b where
                  f :: a -> Bool
                  h :: a -> b -> Bool


                  Now we do still allow us to make two different Foo instances with the same b, but we no longer get the ambiguity since g is now a member of Bar there must be a single instance across the two.



                  This can be done in general by moving the functions that might be ambiguous and moving it to a separate type class.



                  Another way we can use additional type classes to create a second class to enforce the bidirectionality. For the example this would look like:



                  class Bar a b | b -> a

                  class (Bar a b) => Foo a b | a -> b where
                  f :: a -> Bool
                  g :: b -> Bool
                  h :: a -> b -> Bool


                  Here Bar is acts to make b dependent on a preventing us from having the ambiguity. Since Foo requires Bar and Bar allows a to be derived from b, any instance of Foo allows a to be derived from b. This is pretty much what I wanted originally, however it is just a slightly more complex version of Willem Van Onsem's answer.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Jun 3 at 1:55









                  Sriotchilism O'ZaicSriotchilism O'Zaic

                  910620




                  910620



























                      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%2f56410544%2fbidirectional-functional-dependencies%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

                      Club Baloncesto Breogán Índice Historia | Pavillón | Nome | O Breogán na cultura popular | Xogadores | Adestradores | Presidentes | Palmarés | Historial | Líderes | Notas | Véxase tamén | Menú de navegacióncbbreogan.galCadroGuía oficial da ACB 2009-10, páxina 201Guía oficial ACB 1992, páxina 183. Editorial DB.É de 6.500 espectadores sentados axeitándose á última normativa"Estudiantes Junior, entre as mellores canteiras"o orixinalHemeroteca El Mundo Deportivo, 16 setembro de 1970, páxina 12Historia do BreogánAlfredo Pérez, o último canoneiroHistoria C.B. BreogánHemeroteca de El Mundo DeportivoJimmy Wright, norteamericano do Breogán deixará Lugo por ameazas de morteResultados de Breogán en 1986-87Resultados de Breogán en 1990-91Ficha de Velimir Perasović en acb.comResultados de Breogán en 1994-95Breogán arrasa al Barça. "El Mundo Deportivo", 27 de setembro de 1999, páxina 58CB Breogán - FC BarcelonaA FEB invita a participar nunha nova Liga EuropeaCharlie Bell na prensa estatalMáximos anotadores 2005Tempada 2005-06 : Tódolos Xogadores da Xornada""Non quero pensar nunha man negra, mais pregúntome que está a pasar""o orixinalRaúl López, orgulloso dos xogadores, presume da boa saúde económica do BreogánJulio González confirma que cesa como presidente del BreogánHomenaxe a Lisardo GómezA tempada do rexurdimento celesteEntrevista a Lisardo GómezEl COB dinamita el Pazo para forzar el quinto (69-73)Cafés Candelas, patrocinador del CB Breogán"Suso Lázare, novo presidente do Breogán"o orixinalCafés Candelas Breogán firma el mayor triunfo de la historiaEl Breogán realizará 17 homenajes por su cincuenta aniversario"O Breogán honra ao seu fundador e primeiro presidente"o orixinalMiguel Giao recibiu a homenaxe do PazoHomenaxe aos primeiros gladiadores celestesO home que nos amosa como ver o Breo co corazónTita Franco será homenaxeada polos #50anosdeBreoJulio Vila recibirá unha homenaxe in memoriam polos #50anosdeBreo"O Breogán homenaxeará aos seus aboados máis veteráns"Pechada ovación a «Capi» Sanmartín e Ricardo «Corazón de González»Homenaxe por décadas de informaciónPaco García volve ao Pazo con motivo do 50 aniversario"Resultados y clasificaciones""O Cafés Candelas Breogán, campión da Copa Princesa""O Cafés Candelas Breogán, equipo ACB"C.B. Breogán"Proxecto social"o orixinal"Centros asociados"o orixinalFicha en imdb.comMario Camus trata la recuperación del amor en 'La vieja música', su última película"Páxina web oficial""Club Baloncesto Breogán""C. B. Breogán S.A.D."eehttp://www.fegaba.com

                      Vilaño, A Laracha Índice Patrimonio | Lugares e parroquias | Véxase tamén | Menú de navegación43°14′52″N 8°36′03″O / 43.24775, -8.60070

                      Cegueira Índice Epidemioloxía | Deficiencia visual | Tipos de cegueira | Principais causas de cegueira | Tratamento | Técnicas de adaptación e axudas | Vida dos cegos | Primeiros auxilios | Crenzas respecto das persoas cegas | Crenzas das persoas cegas | O neno deficiente visual | Aspectos psicolóxicos da cegueira | Notas | Véxase tamén | Menú de navegación54.054.154.436928256blindnessDicionario da Real Academia GalegaPortal das Palabras"International Standards: Visual Standards — Aspects and Ranges of Vision Loss with Emphasis on Population Surveys.""Visual impairment and blindness""Presentan un plan para previr a cegueira"o orixinalACCDV Associació Catalana de Cecs i Disminuïts Visuals - PMFTrachoma"Effect of gene therapy on visual function in Leber's congenital amaurosis"1844137110.1056/NEJMoa0802268Cans guía - os mellores amigos dos cegosArquivadoEscola de cans guía para cegos en Mortágua, PortugalArquivado"Tecnología para ciegos y deficientes visuales. Recopilación de recursos gratuitos en la Red""Colorino""‘COL.diesis’, escuchar los sonidos del color""COL.diesis: Transforming Colour into Melody and Implementing the Result in a Colour Sensor Device"o orixinal"Sistema de desarrollo de sinestesia color-sonido para invidentes utilizando un protocolo de audio""Enseñanza táctil - geometría y color. Juegos didácticos para niños ciegos y videntes""Sistema Constanz"L'ocupació laboral dels cecs a l'Estat espanyol està pràcticament equiparada a la de les persones amb visió, entrevista amb Pedro ZuritaONCE (Organización Nacional de Cegos de España)Prevención da cegueiraDescrición de deficiencias visuais (Disc@pnet)Braillín, un boneco atractivo para calquera neno, con ou sen discapacidade, que permite familiarizarse co sistema de escritura e lectura brailleAxudas Técnicas36838ID00897494007150-90057129528256DOID:1432HP:0000618D001766C10.597.751.941.162C97109C0155020