Haskell 如何强制严格计算ByteString序列

Haskell 如何强制严格计算ByteString序列,haskell,sequence,strict,bytestring,Haskell,Sequence,Strict,Bytestring,我有以下Haskell类型定义: import Data.Sequence(Seq, length) import Data.ByteString.UTF8(ByteString) type StringSeq = Seq ByteString 我有类型为StringSeq的表达式,我想用deepseq强制对其进行严格计算。所以我需要定义NFData的实例。我做了以下工作: import Control.DeepSeq(NFData, deepseq) instance NFData By

我有以下Haskell类型定义:

import Data.Sequence(Seq, length)
import Data.ByteString.UTF8(ByteString)

type StringSeq = Seq ByteString
我有类型为
StringSeq
的表达式,我想用
deepseq
强制对其进行严格计算。所以我需要定义
NFData
的实例。我做了以下工作:

import Control.DeepSeq(NFData, deepseq)

instance NFData ByteString

instance NFData a => NFData (Seq a) where
  rnf s = rnf (length s)

所以我计算一个序列的长度来强制计算序列本身。这似乎可行,但这是正确的实现方式吗?有更好的吗?计算一个序列的长度会带来太多的开销吗?

为了严格起见,可以定义一个monad

data Strict a = Strict {fromStrict :: !a}
instance Monad Strict where
   return = Strict
   (Strict x) >>= f = f x
好吧,我不认为这实际上遵守了单子定律,但它已经足够接近了。使用此选项可以定义函数

srnf = Strict . rnf 
以致

instance NFData a => NFData (Seq a) where
  rnf s = fromStrict $ (mapM srnf s) >> return ()

未经测试,但应该可以工作(它应该适用于所有可遍历的数据结构)

计算长度是不够的,您需要计算序列内容的标准形式。我建议您使用from
Control.Seq
,它允许您强制任何可折叠结构。然后你可以简单地打电话

mySeq `using` seqFoldable rdeepseq
或定义

instance NFData a => NFData (Seq a) where
    rnf = seqFoldable rdeepseq

计算
Seq
的长度没有任何用处,因为
Seq
已经很严格了。它当然不是一个有效的
NFData
实例,因为它甚至没有试图强制执行
Seq
的内容。而且,
NFData
实际上不太可能是您想要的;你到底想干什么
deepseq
通常对于获得正确的严格行为(和性能)来说“太多、太晚了”。除了shachaf所说的,已经有一个
NFData
实例:
实例NFData a=>NFData(Seq a),其中rnf(Seq xs)=rnf xs
。(啊,仅在7.6.1附带的
容器中,不适用于早期版本),但是,在将
seq
放入
seq
之前,您所要做的就是
ByteString
s。由于
Seq
的脊柱狭窄,这确保了完整的评估。@Daniel Fischer:但是
Seq
是函数的结果:返回
Seq
的函数应用程序也必须进行评估。我必须检查
seq
在这里是否足够,也许不够?我只是好奇,
newtype Strict a=Strict{fromStrict::a}
也很严格,性能稍好一些吗?
newtype
不严格。它根本不存在。关键是
>>=
定义中的模式匹配在标识单子不匹配时强制求值。您可以使用您定义的新类型,但必须说
Strict x>=f=seq x$f x