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问题。