Haskell 序列号的构造性处理
我正在实现一个简单的协议,其中消息的序列号必须在消息之间严格递增。为了处理这个问题,我写了:Haskell 序列号的构造性处理,haskell,newtype,Haskell,Newtype,我正在实现一个简单的协议,其中消息的序列号必须在消息之间严格递增。为了处理这个问题,我写了: newtype SequenceNo = SequenceNo Int64 deriving (Show, Eq) validSequence :: SequenceNo -> SequenceNo -> Bool validSequence (SequenceNo firstS) (SequenceNo secondS) = firstS + 1 == secondS 我是这样用的
newtype SequenceNo = SequenceNo Int64
deriving (Show, Eq)
validSequence :: SequenceNo -> SequenceNo -> Bool
validSequence (SequenceNo firstS) (SequenceNo secondS) = firstS + 1 == secondS
我是这样用的:
applyUpdates :: ProtocolState -> UpdateMessage -> Either String ProtocolState
applyUpdates oldState upd =
if validSequence (seqNo oldState) (updSeqNo upd)
then Right (ProtocolState {seqNo=updSeqNo upd, …})
else Left "invalid sequence"
但这和isJust::也许a->Bool有着同样的布尔盲问题。我怎样才能做得更好?我想到了以下几点:
data SequenceTag = Initial | Following
newtype SequenceNo (t :: SequenceTag) = SequenceNo Int64
deriving (Show, Eq)
advanceSequence ::
SequenceNo 'Initial ->
SequenceNo 'Following ->
Maybe (SequenceNo 'Initial)
advanceSequence (SequenceNo firstS) (SequenceNo secondS)
| firstS + 1 == secondS = Just (SequenceNo secondS)
| otherwise = Nothing
用法:
applyUpdates :: ProtocolState -> UpdateMessage -> Either String ProtocolState
applyUpdates oldState upd =
case advanceSequence (seqNo oldState) (updSeqNo upd) of
Just s -> Right (ProtocolState {seqNo=s, …})
Nothing -> Left "invalid sequence"
更好,但还是有点笨重?我只想定义一个与Bool同构的类型,但使用更具描述性的构造函数名称
date Validity = Valid | Invalid
然后编写一个函数,返回由参数表示的序列类型:
classifySequence :: SequenceNo -> SequenceNo -> Validity
classifySequence (SequenceNo x) (SequenceNo y) | x + 1 == y = Valid
| otherwise = Invalid
如果您为您的类型定义一个枚举实例,那么就更简单了
newtype SequenceNo = SequenceNo Int64 deriving (Show, Read, Eq, Enum)
classifySequence :: SequenceNo -> SequenceNo -> Validity
classifySequence x y | succ x == y = Valid
| otherwise = Invalid
无论哪种方式,您都可以定义
applyUpdates :: ProtocolState -> UpdateMessage -> Either String ProtocolState
applyUpdates oldState upd =
case validSequence (seqNo oldState) (updSeqNo upd) of
Valid -> Right (ProtocolState {seqNo=updSeqNo upd, …})
Invalid -> Left "invalid sequence"
尽管您也可以考虑一个显式的错误类型:
data SequenceError = InvalidSequence
applyUpdates :: ProtocolState -> UpdateMessage -> Either SequenceError ProtocolState
applyUpdates oldState upd =
case validSequence (seqNo oldState) (updSeqNo upd) of
Valid -> Right (ProtocolState {seqNo=updSeqNo upd, …})
Invalid -> Left InvalidSequence
我不认为这只是布尔盲的一个例子。返回值直接回答函数名隐含的问题。一个更好的例子是filter:您是保留满足谓词的每个值,还是丢弃每个值?为什么advanceSequence在您的答案中将潜在返回值作为参数?为SequenceNo提供一个枚举实例,然后定义advanceSequence::SequenceNo->SequenceNo有什么错;advanceSequence=succ?发送端正在生成序列号,我的代码只验证我收到的序列号。关于布尔盲,我的意思是我们通常更喜欢写一个→ …; 没有什么→ … 而不是如果只是m那么…否则…那仍然不是布尔盲;对于返回值True的含义没有任何混淆。如果您还不知道过滤器的作用,那么从类型上看它的作用就不明显了。过滤器::a->Bool->[a]->[a]是模糊的;Keepipftrue::a->Bool->[a]->[a]不是,即使它的类型是Bool;您从validSequence切换到advanceSequence的事实表明您有XY问题。