为什么 `data` 会导致无限循环而 `newtype` 不会
Why `data` cause an infinite loop while `newtype` not
我正在按照教程 programming with arrows 学习 Arrow
。我根据论文输入了以下代码,除了 SF
是由 data
定义的,而不是论文中的 newtype
( 实际上,我做了这个偶然更改,因为我从内存中输入了代码):
import Control.Category
import Control.Arrow
import Prelude hiding (id, (.))
data SF a b = SF { runSF :: [a] -> [b] } -- this is the change, using data instead of newtype as in the paper
-- The folowing code is the same as in the paper
instance Category SF where
id = SF $ \x -> x
(SF f) . (SF g) = SF $ \x -> f (g x)
instance Arrow SF where
arr f = SF $ map f
first (SF f) = SF $ unzip >>> first f >>> uncurry zip
instance ArrowChoice SF where
left (SF f) = SF $ \xs -> combine xs (f [y | Left y <- xs])
where
combine (Left _ : ys) (z:zs) = Left z : combine ys zs
combine (Right y : ys) zs = Right y : combine ys zs
combine [] _ = []
delay :: a -> SF a a
delay x = SF $ init . (x:)
mapA :: ArrowChoice a => a b c -> a [b] [c]
mapA f = arr listcase >>>
arr (const []) ||| (f *** mapA f >>> arr (uncurry (:)))
listcase :: [a] -> Either () (a, [a])
listcase [] = Left ()
listcase (x:xs) = Right (x, xs)
当我加载ghci
中的文件并执行runSF (mapA (delay 0)) [[1,2,3],[4,5,6]]
时,它触发了一个无限循环并最终耗尽了内存。如果我将 data
改回 newtype
,一切正常。同样的问题发生在 ghc 8.0.2、8.2.2 和 8.6.3 中。
我把代码编译成可执行文件也存在同样的问题
我认为在定义只有一个字段的数据结构时,data
和 newtype
之间的区别在于运行时成本。但这个问题似乎暗示着它们之间更多的不同。或者可能有一些我没有注意到的关于 Arrow
类型的东西-class.
任何人都可以有任何想法吗?非常感谢!
我们来看这个例子。
data A = A [Int]
deriving (Show)
cons :: Int -> A -> A
cons x (A xs) = A (x:xs)
ones :: A
ones = cons 1 ones
我们希望 ones
应该是 A [1,1,1,1...]
,因为我们所做的只是将列表包装在 data
构造函数中。但我们错了。回想一下, 模式匹配对于 data
构造函数是严格的。也就是说,cons 1 undefined = undefined
而不是 A (1 : undefined)
。因此,当我们尝试评估 ones
时,cons
模式匹配其第二个参数,这导致我们评估 ones
... 我们遇到了问题。
newtype
s 不要这样做。在 运行 时间 newtype
构造函数是不可见的,所以就好像我们在普通列表上编写了等效程序一样
cons :: Int -> [Int] -> [Int]
cons x ys = x:ys
ones = cons 1 ones
这非常高效,因为当我们尝试计算 ones
时,在我们和 ones
的下一个计算之间有一个 :
构造函数。
您可以通过使数据构造函数模式匹配延迟来恢复 newtype
语义:
cons x ~(A xs) = A (x:xs)
这是您的代码的问题(我 运行 在做这件事时遇到了这个确切的问题)。有几个原因 data
模式匹配默认是严格的;我看到的最引人注目的是,如果该类型具有多个构造函数,则模式匹配将无法实现。为了修复一些微妙的 GC 泄漏,惰性模式匹配也有一个小的 运行 时间开销;评论中链接的详细信息。
我正在按照教程 programming with arrows 学习 Arrow
。我根据论文输入了以下代码,除了 SF
是由 data
定义的,而不是论文中的 newtype
( 实际上,我做了这个偶然更改,因为我从内存中输入了代码):
import Control.Category
import Control.Arrow
import Prelude hiding (id, (.))
data SF a b = SF { runSF :: [a] -> [b] } -- this is the change, using data instead of newtype as in the paper
-- The folowing code is the same as in the paper
instance Category SF where
id = SF $ \x -> x
(SF f) . (SF g) = SF $ \x -> f (g x)
instance Arrow SF where
arr f = SF $ map f
first (SF f) = SF $ unzip >>> first f >>> uncurry zip
instance ArrowChoice SF where
left (SF f) = SF $ \xs -> combine xs (f [y | Left y <- xs])
where
combine (Left _ : ys) (z:zs) = Left z : combine ys zs
combine (Right y : ys) zs = Right y : combine ys zs
combine [] _ = []
delay :: a -> SF a a
delay x = SF $ init . (x:)
mapA :: ArrowChoice a => a b c -> a [b] [c]
mapA f = arr listcase >>>
arr (const []) ||| (f *** mapA f >>> arr (uncurry (:)))
listcase :: [a] -> Either () (a, [a])
listcase [] = Left ()
listcase (x:xs) = Right (x, xs)
当我加载ghci
中的文件并执行runSF (mapA (delay 0)) [[1,2,3],[4,5,6]]
时,它触发了一个无限循环并最终耗尽了内存。如果我将 data
改回 newtype
,一切正常。同样的问题发生在 ghc 8.0.2、8.2.2 和 8.6.3 中。
我把代码编译成可执行文件也存在同样的问题
我认为在定义只有一个字段的数据结构时,data
和 newtype
之间的区别在于运行时成本。但这个问题似乎暗示着它们之间更多的不同。或者可能有一些我没有注意到的关于 Arrow
类型的东西-class.
任何人都可以有任何想法吗?非常感谢!
我们来看这个例子。
data A = A [Int]
deriving (Show)
cons :: Int -> A -> A
cons x (A xs) = A (x:xs)
ones :: A
ones = cons 1 ones
我们希望 ones
应该是 A [1,1,1,1...]
,因为我们所做的只是将列表包装在 data
构造函数中。但我们错了。回想一下, 模式匹配对于 data
构造函数是严格的。也就是说,cons 1 undefined = undefined
而不是 A (1 : undefined)
。因此,当我们尝试评估 ones
时,cons
模式匹配其第二个参数,这导致我们评估 ones
... 我们遇到了问题。
newtype
s 不要这样做。在 运行 时间 newtype
构造函数是不可见的,所以就好像我们在普通列表上编写了等效程序一样
cons :: Int -> [Int] -> [Int]
cons x ys = x:ys
ones = cons 1 ones
这非常高效,因为当我们尝试计算 ones
时,在我们和 ones
的下一个计算之间有一个 :
构造函数。
您可以通过使数据构造函数模式匹配延迟来恢复 newtype
语义:
cons x ~(A xs) = A (x:xs)
这是您的代码的问题(我 运行 在做这件事时遇到了这个确切的问题)。有几个原因 data
模式匹配默认是严格的;我看到的最引人注目的是,如果该类型具有多个构造函数,则模式匹配将无法实现。为了修复一些微妙的 GC 泄漏,惰性模式匹配也有一个小的 运行 时间开销;评论中链接的详细信息。