序列号的建设性处理

Constructive handling of sequence numbers

我正在实施一个简单的协议,其中消息具有一个序列号,该序列号必须在消息之间严格递增。为了解决这个问题,我写道:

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 :: Maybe 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

然后编写一个函数,returns 种类 由参数表示的序列:

classifySequence :: SequenceNo -> SequenceNo -> Validity
classifySequence (SequenceNo x) (SequenceNo y) | x + 1 == y = Valid
                                               | otherwise = Invalid

如果您为您的类型定义一个 Enum 实例,那就更简单了。

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"

尽管您也可以考虑显式错误类型:

<b>data SequenceError = InvalidSequence</b>

applyUpdates :: ProtocolState -> UpdateMessage -> Either <b>SequenceError</b> ProtocolState
applyUpdates oldState upd =
  case validSequence (seqNo oldState) (updSeqNo upd) of
    Valid -> Right (ProtocolState {seqNo=updSeqNo upd, …})
    Invalid -> Left <b>InvalidSequence</b>