Why only short circuit multiplication on one side

I messed with fix , and after messing with it, I met some strange behavior, namely that 0 * undefined - *** Exception: Prelude.undefined and undefined * 0 - 0 . It also means that fix (0 *) is *** Exception: <<loop>> , and fix (* 0) is 0 .

After playing with him, it seems that the reason lies in the fact that it is not trivial to make this short circuit in both directions, since it does not make much sense without any strange parallel computing and starts with the first non-lower one.

Is this the kind of thing in other places (reflective functions that are not reflective for lower values), and what can I reliably rely on? There is also a practical way to make (0 *) and (* 0) evaluate to zero regardless of the value passed.

+46
haskell short-circuiting
Mar 17 '16 at 0:47
source share
3 answers

Your reasoning is correct. There is an unamb package that provides the tools for this kind of parallel computing that you are referring to. In fact, it offers Data.Unamb.pmult , which in parallel tries to check whether each operand is 1 or 0, and if this immediately gives a result. This parallel approach is likely to be much slower in most cases for simple arithmetic!

Short circuit (*) occurs only in version 7.10 of the GHC. This happened as a result of changes to the implementation of the Integer type in this version of GHC. This excess laziness was usually seen as a performance error (since it interferes with rigor analysis and may even lead to spatial leaks in theory), so it will be removed in GHC 8.0.

+41
Mar 17 '16 at 0:57
source share

Take for example

 (if expensiveTest1 then 0 else 2) * (if expensiveTest2 then 0 else 2) 

You need to choose a side to evaluate. If expensiveTest2 is an infinite loop, you can never determine if it has the right side 0 or not, so you cannot say whether to short-circuit the right side so that you can never look at the left side. You cannot check if both sides have 0 once.

As for whether you can rely on a short circuit to act in a certain way, just keep in mind that undefined and error act just like an infinite loop if you are not using IO. Therefore, you can test a short circuit and laziness with undefined and error . In general, the behavior of a short circuit depends on function to function. (There are also different levels of laziness. undefined and Just undefined can give different results.)

See more details.

+6
Mar 17 '16 at 1:11
source share

Actually it seems that fix (* 0) == 0 only works for Integer , if you run fix (* 0) :: Double or fix (* 0) :: Int , you still get ***Exception <<loop>>

This is because in instance Num Integer , (*) is defined as (*) = timesInteger

timesInteger defined in Data.Integer

 -- | Multiply two 'Integer's timesInteger :: Integer -> Integer -> Integer timesInteger _ (S# 0#) = S# 0# timesInteger (S# 0#) _ = S# 0# timesInteger x (S# 1#) = x timesInteger (S# 1#) y = y timesInteger x (S# -1#) = negateInteger x timesInteger (S# -1#) y = negateInteger y timesInteger (S# x#) (S# y#) = case mulIntMayOflo# x# y# of 0# -> S# (x# *# y#) _ -> timesInt2Integer x# y# timesInteger x@(S# _) y = timesInteger yx -- no S# as first arg from here on timesInteger (Jp# x) (Jp# y) = Jp# (timesBigNat xy) timesInteger (Jp# x) (Jn# y) = Jn# (timesBigNat xy) timesInteger (Jp# x) (S# y#) | isTrue# (y# >=# 0#) = Jp# (timesBigNatWord x (int2Word# y#)) | True = Jn# (timesBigNatWord x (int2Word# (negateInt# y#))) timesInteger (Jn# x) (Jn# y) = Jp# (timesBigNat xy) timesInteger (Jn# x) (Jp# y) = Jn# (timesBigNat xy) timesInteger (Jn# x) (S# y#) | isTrue# (y# >=# 0#) = Jn# (timesBigNatWord x (int2Word# y#)) | True = Jp# (timesBigNatWord x (int2Word# (negateInt# y#))) 

Look at the code above, if you run (* 0) x , then timesInteger _ (S# 0#) will match so that x will not be evaluated, and if you run (0 *) x , then when checking if timesInteger _ (S# 0#) x will evaluate and cause an infinite loop

We can use the code below to verify it:

 module Test where import Data.Function(fix) -- fix (0 ~*) == 0 -- fix (~* 0) == ***Exception<<loop>> (~*) :: (Num a, Eq a) => a -> a -> a 0 ~* _ = 0 _ ~* 0 = 0 x ~* y = x ~* y -- fix (0 *~) == ***Exception<<loop>> -- fix (*~ 0) == 0 (*~) :: (Num a, Eq a) => a -> a -> a _ *~ 0 = 0 0 *~ _ = 0 x *~ y = x *~ y 

GHCI has something even more interesting:

 *Test> let x = fix (* 0) *Test> x 0 *Test> x :: Double *** Exception: <<loop>> *Test> 
+6
Mar 17 '16 at 3:46
source share



All Articles