Ответ 1
Для первой части этого ответа :set -XNoMonomorphismRestriction
в ghci. Это будет объяснено позже.
Наивно можно было бы ожидать, что в Haskell let x = 5 in (x + 1,x + 2)
всегда будет эквивалентно (\x → (x + 1, x + 2)) 5
. Но у них разные типы!
let x = 5 in (x + 1,x + 2) :: (Num a, Num b) => (a, b)
(\x -> (x + 1,x + 2)) 5 :: Num b => (b, b)
Причина - особенность Haskell, называемого let-bound полиморфизмом. В отличие от lambda-связанных идентификаторов, идентификаторы, связанные в let
могут быть созданы экземплярами по-разному в теле let
. Например:
ghci> let f = id in (f True, f 'a')
(True,'a')
ghci> (\f -> (f True, f 'a')) id
*** ERROR ***
Теперь вы не указали типу подписи для функции fib
, и тот, который выведен, является чем-то вроде
fib :: (Ord a, Num a) => a -> a
который будет работать для разных экземпляров Num
таких как Int
, Float
и т.д.
Но из-за этого, когда вы пишете x 'seq' x
, ghci не может быть уверен, что два x
фактически одного типа! И если они могут быть разными, тогда их нельзя разделить.
То, что (\x → x 'seq' x) (fib 30)
действительно имеет общий доступ. Поскольку x
привязан к lambda, компилятор уверен, что оба вхождения действительно имеют одинаковое значение. То же самое для let x = (fib 30) :: Int in x 'seq' x
потому что мы удалили полиморфизм с использованием явного типа.
Там другой выход. Включение -XMonomorphismRestriction
расширение увеличивает количество типа невыплаты, в результате чего let
выражение более мономорфное чем можно было бы ожидать. Этого должно быть достаточно для восстановления совместного использования в этом случае.