Я думал, что -XStrict
должен был превратить GHC в Strict Haskell, поэтому я попробовал тест на бесконечную последовательность Фибоначчи.
my_zipWith f x [] = []
my_zipWith f [] y = []
my_zipWith f (x:xt) (y:yt) = f x y : my_zipWith f xt yt
test_fibs n =
let fibs = 0 : 1 : my_zipWith (+) fibs (tail fibs) in
take n fibs
main = do
putStr $ show $ test_fibs 15
чтобы увидеть, взорвется ли он в памяти, но это не так:
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.0.2
$ ghc -XStrict fibs.hs && ./fibs
[1 of 1] Compiling Main ( fibs.hs, fibs.o )
Linking fibs ...
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377]
Что я делаю не так?
@chi Я понял это из ответа. Мне просто любопытно, можно ли сделать :
, ++
, zipWith
и другие функции списка строгими.
@MaxB: Нет, []
определяется как ленивый. Если вам нужны строгие версии базовых типов, есть Data.Strict.List.List от strict-base
(хотя vector
предпочтительнее) и пакет strict для некоторых других. У них есть экземпляры стандартных классов, таких как Foldable
. zipWith
не перегружен в base
, но есть Zip в keys
(отдельно от старого category-extras
).
Strict
прагма конечно позволяет GHC оценивать все строго, но только в Weak Head Normal Form. Например:
(a, b) = (error "a", error "b")
Если приведенный выше код существует в прагме Strict
, возникает любая ошибка. Давайте посмотрим ваш код:
test_fibs n =
let fibs = 0 : 1 : my_zipWith (+) fibs (tail fibs) in
take n fibs
fibs
вызывается рекурсивно, но в списке минусов, так что теперь весь список WHNF. Вот почему ваш код не складывался.
Этот пост поможет и вам. Наслаждайтесь рекурсией и ленью!
Добавлять:
Простой способ использования deepseq:
{-# LANGUAGE Strict #-}
import Control.DeepSeq
my_zipWith f x [] = []
my_zipWith f [] y = []
my_zipWith f (x:xt) (y:yt) = force $ f x y : my_zipWith f xt yt
test_fibs :: Int -> [Int]
test_fibs n =
let fibs = 0 : 1 : my_zipWith (+) fibs (tail fibs) in
force $ take n fibs
main = do
putStr $ show $ test_fibs 15
force
определяется как force x = x `deepSeq` x
, deepSeq
глубоко буквально оценивает выражение до NF (нормальная форма). Это преобразование достигается с помощью GHC.Generics
. Если конвертировать вручную, просто нужно оценить данные внутри, поэтому можно переписать следующее:
{-# LANGUAGE Strict #-}
my_zipWith f x [] = []
my_zipWith f [] y = []
my_zipWith f (x:xt) (y:yt) = f x y : go
where
go = my_zipWith f xt yt
test_fibs n =
let
fib2 = my_zipWith (+) fibs (tail fibs)
fibs = 0 : 1 : fib2
in
take n fibs
main = do
putStr $ show $ test_fibs 15
Но на самом деле они не могут складываться. Потому что GHC может обнаружить бесконечный цикл, но это уже другая история.
Так есть ли способ использовать строгие списки?
Strict
примерно добавляет аннотацию строгости!
везде, где она может быть добавлена в ваш файл. Библиотеки, скомпилированные без этого флага, не затрагиваются. Здесь вы используете стандартный тип списка[a]
, который остается нестрогим. Если вы определили свой собственный тип списка (и связанные с нимzipWith
,tail
и т. д.), я думаю, вы получите ожидаемое поведение (?).