Which one is XOR considered best in Haskell

I would like to know what is the most common way in Haskell.

The first clearly states that we want two arguments (most of the time).

The second involves calling the function ( id ) in the second sentence, so it should be less efficient, since in the first implementation we can simply return the second argument.

So, I am inclined to think that the first is better and should be what you need to choose: it is easier to read and find out what it does [1], and save the function call.

But I'm new to Haskell, maybe the compiler is optimizing this extra call.

 xor :: Bool -> Bool -> Bool xor True x = not x xor False x = x xor True = not xor False = id 

Also, I would like to know if I can replace both False with a wildcard.

So what is good practice at Haskell. Maybe a different implementation?

[1] We omit there that this is a well-known function, imagine that it is a nontrivial function.

thanks

+7
optimization haskell
source share
3 answers

Of course, this depends on the compiler and the parameters passed to the compiler.

In this particular example, if you compile without optimizations, GHC creates the code as you wrote it, so the second version contains an id resp call. until not . This is slightly less efficient than the first version, which then only contains a call to not :

 Xors.xor1 :: GHC.Types.Bool -> GHC.Types.Bool -> GHC.Types.Bool [GblId, Arity=2] Xors.xor1 = \ (ds_dkm :: GHC.Types.Bool) (x_aeI :: GHC.Types.Bool) -> case ds_dkm of _ { GHC.Types.False -> x_aeI; GHC.Types.True -> GHC.Classes.not x_aeI } Xors.xor2 :: GHC.Types.Bool -> GHC.Types.Bool -> GHC.Types.Bool [GblId, Arity=1] Xors.xor2 = \ (ds_dki :: GHC.Types.Bool) -> case ds_dki of _ { GHC.Types.False -> GHC.Base.id @ GHC.Types.Bool; GHC.Types.True -> GHC.Classes.not } 

(calls are still in the produced assembly, but the kernel is more readable, so I publish only this).

But with optimization, both functions are compiled with the same kernel (and from there to the same machine code),

 Xors.xor2 = \ (ds_dkf :: GHC.Types.Bool) (eta_B1 :: GHC.Types.Bool) -> case ds_dkf of _ { GHC.Types.False -> eta_B1; GHC.Types.True -> case eta_B1 of _ { GHC.Types.False -> GHC.Types.True; GHC.Types.True -> GHC.Types.False } } 

GHC eta - extended the second version and introduced calls on id and not , you will get a clean pattern match.

Whether the second equation is False or a wildcard has nothing to do with any version, with or without optimization.

perhaps the compiler optimizes this extra call.

If you ask him to optimize, in simple cases like this, the GHC will eliminate the extra call.

suppose this is a nontrivial function.

Here is a possible problem. If the code is nontrivial, the compiler will not be able to eliminate all the calls made by defining the function, and not all arguments are provided. GHC does a good job of this and embeds calls, however, so you need a lot of nontriviality to make GHC refuse calls to simple functions that it knows when compiling your code (it, of course, can never connect to functions that he does. I do not know how to implement compilation of the module in question.

If this is critical code, always check what code the compiler produces for the GHC corresponding -ddump-simpl flags to get the kernel created after optimizations, and -ddump-asm to get the build done.

+11
source share

For ease of reading, I will try to avoid pattern matching and define a function using a single equation that expresses something interesting about the function that needs to be defined. This is not always possible, but there are many options for this example:

  • xor = (/=)
  • xor ab = a /= b
  • xor ab = not (a == b)
  • xor ab = (a && not b) || (not a && b)
  • xor ab = (a || b) && not (a && b)
  • xor ab = odd (fromEnum a + fromEnum b)
+13
source share

So, I am inclined to think that the first is better and should be the one to be chosen: it is easier to read and figure out what it does

I agree with readability. However, the second is a very idiomatic Haskell and quite easy to read for experienced programmers: not doing this trivial abbreviation eta is rather suspicious and can actually be distracted from the intention. Therefore, for an optimized version, I would prefer to write it out explicitly:

 True `xor` False = True False `xor` True = True _ `xor` _ = False 

However, if such an alternative is significantly less readable than the most idiomatic, you should consider replacing it, but adding hints so that the compiler can still optimize it to the ideal version. As Daniel Fisher demonstrated, the GHC is pretty smart on its own and often gets this right without any help; otherwise, it may help to add the INLINE and / or RULES . It’s not easy to figure out how to do this to get the best performance, but the same is true for writing fast Haskell98 code.

+4
source share

All Articles