Why does multiplication only short circuit on one side

后端 未结 3 524
独厮守ぢ
独厮守ぢ 2021-02-03 17:39

I was messing around with fix and after messing around with it I came across some weird behavior, namely that 0 * undefined is *** Exception: Pre

3条回答
  •  猫巷女王i
    2021-02-03 18:05

    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 <>

    That's because in instance Num Integer, (*) is defined as (*) = timesInteger

    timesInteger is 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 y x
    -- no S# as first arg from here on
    timesInteger (Jp# x) (Jp# y) = Jp# (timesBigNat x y)
    timesInteger (Jp# x) (Jn# y) = Jn# (timesBigNat x y)
    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 x y)
    timesInteger (Jn# x) (Jp# y) = Jn# (timesBigNat x y)
    timesInteger (Jn# x) (S# y#)
      | isTrue# (y# >=# 0#) = Jn# (timesBigNatWord x (int2Word# y#))
      | True       = Jp# (timesBigNatWord x (int2Word# (negateInt# y#)))
    

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

    We can use below code to test it:

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

    There is something even more interesting, in GHCI:

    *Test> let x = fix (* 0) 
    *Test> x 
    0
    *Test> x :: Double 
    *** Exception: <>
    *Test>  
    

提交回复
热议问题