Да, они выполняют то же самое, но только случайно. В GHCI:
> :i .
(.) :: (b -> c) -> (a -> b) -> (a -> c) -- Defined in ‘GHC.Base’
infixr 9 .
Поскольку infixr
, foo . foo . foo
разбирает, как foo . (foo . foo)
, и поэтому мы можем писать ярлык foo . foo . foo . ...
миллиард раз foldr
. Итак:
> 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
Он возвращается почти сразу. С другой стороны, следующие два повисеть:
> foldl (.) id (replicate 1000000000 foo)()
> foldr (flip (.)) id (replicate 1000000000 foo)()
Однако, я должен еще раз подчеркнуть, что это совпадение реализации GHC, а не гарантия сделала семантику языка. Так получилось, что реализация GHC imprecise exceptions быстро замечает, что это будет ошибка (и выбрасывает самую левую ошибку foo
) в случае foldr (.)
, но не так быстро отмечает foldl (.)
. И, конечно же, в случае, когда foo
является не просто ошибкой, но и должен выполнять некоторые фактические вычисления, что вычисление нужно будет выполнять столько раз, сколько оно появляется в строке композиций - GHC довольно удивительно, но не волшебный ,
Вы конкретно спрашиваете о 'a -> b -> c', где мы можем видеть по типу, что единственный возможный результат - это не прерывание? – sepp2k
Любое количество foo, сложенных вместе, будет работать почти мгновенно и терпеть неудачу. Возможно, переосмыслите свой вопрос? Как насчет 'succ. succ'? –