"foo" 是 "a -> b -> c"。 "foo . foo" 也是 "a -> b -> c"。 Haskell 在速度方面是否完全相同?
"foo" is "a -> b -> c". "foo . foo" is also "a -> b -> c". Does Haskell treat them exactly the same in terms of speed?
foo :: a -> b -> c
foo = undefined
ghci> :t foo
foo :: a -> b -> c
ghci> :t foo . foo
foo . foo :: a -> b -> c
ghci> :t foo . foo . foo
foo . foo . foo :: a -> b -> c
以此类推
类型相同。我们还清楚地看到,仅通过查看类型,这三者都是等价的。他们有相同的结果。
但是速度呢?例如,foo
是否会自动(没有优化)运行 与 .
组合一百万次,就像 foo
一样快?如果不是,优化(O1 或 O2)是否会如此?
是的,他们表现相同,但只是偶然。在 ghci:
> :i .
(.) :: (b -> c) -> (a -> b) -> (a -> c) -- Defined in ‘GHC.Base’
infixr 9 .
因为它是 infixr
,foo . foo . foo
解析为 foo . (foo . foo)
,所以我们可以用 foldr
快捷地写十亿次 foo . foo . foo . ...
。所以:
> let foo :: a -> b -> c; foo = undefined
> foldr (.) id (replicate 1000000000 foo) ()
*** Exception: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries/base/GHC/Err.hs:79:14 in base:GHC.Err
undefined, called at <interactive>:1:5 in interactive:Ghci1
几乎是立即 returns。另一方面,下面两个挂了一会儿:
> foldl (.) id (replicate 1000000000 foo) ()
> foldr (flip (.)) id (replicate 1000000000 foo) ()
不过,我必须再次强调,这是GHC实现的巧合,而不是语言语义的保证。碰巧 GHC 的 imprecise exceptions 实现很快注意到在 foldr (.)
的情况下这将是一个错误(并抛出最左边的 foo
的错误),但不会尽快通知 foldl (.)
。当然,在 foo
不仅是一个错误,而且必须进行一些实际计算的情况下,该计算将需要执行与它在组合字符串中出现的次数一样多的次数——GHC 非常了不起, 但并不神奇。
foo :: a -> b -> c
foo = undefined
ghci> :t foo
foo :: a -> b -> c
ghci> :t foo . foo
foo . foo :: a -> b -> c
ghci> :t foo . foo . foo
foo . foo . foo :: a -> b -> c
以此类推
类型相同。我们还清楚地看到,仅通过查看类型,这三者都是等价的。他们有相同的结果。
但是速度呢?例如,foo
是否会自动(没有优化)运行 与 .
组合一百万次,就像 foo
一样快?如果不是,优化(O1 或 O2)是否会如此?
是的,他们表现相同,但只是偶然。在 ghci:
> :i .
(.) :: (b -> c) -> (a -> b) -> (a -> c) -- Defined in ‘GHC.Base’
infixr 9 .
因为它是 infixr
,foo . foo . foo
解析为 foo . (foo . foo)
,所以我们可以用 foldr
快捷地写十亿次 foo . foo . foo . ...
。所以:
> let foo :: a -> b -> c; foo = undefined
> foldr (.) id (replicate 1000000000 foo) ()
*** Exception: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries/base/GHC/Err.hs:79:14 in base:GHC.Err
undefined, called at <interactive>:1:5 in interactive:Ghci1
几乎是立即 returns。另一方面,下面两个挂了一会儿:
> foldl (.) id (replicate 1000000000 foo) ()
> foldr (flip (.)) id (replicate 1000000000 foo) ()
不过,我必须再次强调,这是GHC实现的巧合,而不是语言语义的保证。碰巧 GHC 的 imprecise exceptions 实现很快注意到在 foldr (.)
的情况下这将是一个错误(并抛出最左边的 foo
的错误),但不会尽快通知 foldl (.)
。当然,在 foo
不仅是一个错误,而且必须进行一些实际计算的情况下,该计算将需要执行与它在组合字符串中出现的次数一样多的次数——GHC 非常了不起, 但并不神奇。