Haskell 模拟替代版本编号方法的数据结构

Haskell data structure to simulate alternative version numbering approach

我正在尝试在 Haskell 中模拟 version numbering in more generic terms。我正在寻找一个 Haskell 数据结构来表示我自己定义的结构 (VersionNumberWithMaybe)。

data VersionCompoundWithMaybe = Maybe Int -- Just n: 0, 1, 2, 3 ...
                                          -- Nothing: x
maybeToString :: Maybe Int -> String
maybeToString (Just n) = (show n)
maybeToString Nothing = "x"

data VersionNumberWithMaybe = VC (Maybe Int)             -- VersionCompound: x, 0, 1, 2, 3 ...
                | VNL (Maybe Int) VersionNumberWithMaybe -- VersionNumberLeft: x.x, x.0, x.1, x.2, ... , 1.0, 1.1, 1.2, ... 1.x.x, 2.x.x, 3.x.x, ...
                | VNR VersionNumberWithMaybe (Maybe Int) -- VersionNumberRight: the same as above, only underlying structure is different for simple parsing pursposes: x.x, x.0, x.1, x.2, ... , 1.0, 1.1, 1.2, ... 1.x.x, 2.x.x, 3.x.x, ...
                deriving (Show)

versionNumberWithMaybeToString :: VersionNumberWithMaybe -> String
versionNumberWithMaybeToString (VNL vc vn) = (maybeToString vc) ++ "." ++ (versionNumberWithMaybeToString vn)
versionNumberWithMaybeToString (VNR vn vc) = (versionNumberWithMaybeToString vn) ++ "." ++ (maybeToString vc)
versionNumberWithMaybeToString (VC vc) = (maybeToString vc)

是否有类似的标准库实现可以将字符串解析为该数据结构并在左右表示之间进行转换?我将不胜感激 comments/ideas。提前致谢!

PS。我需要 VNLVNR 选项,以便我可以按以下方式比较版本号:x.x.3 == x.3 == 3。我假设只有 VNR 版本可以用于该目的:

instance Eq VersionNumberWithMaybe where
    (VC vc1) == (VC vc2) = (vc1 == vc2)
    ( VNL vc1 vn1 ) == ( VNL vc2 vn2 ) = (vc1 == vc2 && vn1 == vn2)
    ( VNR vn1 vc1 ) == ( VNR vn2 vc2 ) = (vc1 == vc2 && vn1 == vn2)

    ( VNL _ (VC vc1) ) == ( VC vc2 ) = vc1 == vc2 
    ( VC vc1 ) == ( VNL _ (VC vc2)) = vc1 == vc2

    ( VNR _ vc1 ) == (VC vc2) = vc1 == vc2
    ( VC vc1 ) == (VNR _ vc2) = vc1 == vc2

鉴于 Eq 的定义,以下比较将按预期正常工作:

VNR (VNR ( VC Nothing ) Nothing) (Just 3) == VNR (VC Nothing) (Just 3) -- x.x.3 == x.3 -> True
VNR (VC Nothing) (Just 3) == VC (Just 3) -- x.3 == 3 -> True

虽然使用 VNL 它不会按预期工作,因为它允许进行 3.x.x == 3.x -> True 等比较

PS2。感谢大家的评论。我开始重新考虑我是否真的需要 VNRVNL(而不仅仅是 VNL)来实现我想要的。我正在评估哪种方法最好只留下一个并避免整个 conversion_between_representations 头痛。

为了表示定义为由句点分隔的整数序列的版本号,可以简单地使用整数列表而不是实现自己的列表类型:

newtype LTRVersion = MkLTRVersion { unLTRVersion :: [Int] }
  deriving (Show, Eq, Ord)

versionToString = intercalate "." . map show . unLTRVersion

这允许任意版本的形式 "x.y.z" 用于任意数量的版本 "chunks"。正确的排序关系是可导的,方便。

(注意:人们可能认为自然数会是一种更精确的表示,但 Haskell 会很高兴地允许您通过尝试从较小的数字中减去较大的数字来生成运行时错误,因此 YMMV。 Int 也可用于前奏,而自然数则不可。)

要实现从右到左的版本控制方案,可以使用 snoc 列表或反转列表以进行比较(使用 Data.Ord 中的 comparing):

newtype RTLVersion = MkRTLVersion { unRTLVersion :: [Int] }

instance Ord RTLVersion where
  compare = comparing (reverse . unRTLVersion)

但是,两者都只允许整数版本号。比较随意的是:

newtype Version = MkVersion [Either Int String]
  deriving (Show, Eq)

instance Ord Version

versionToString :: Version -> String

留作 reader 的练习。

版本号缺失的另一种概括 "chunks" 可以表示每个块可能存在:

newtype MayVersion = MkMayVersion [Maybe Int]

我不确定显示、等同或比较这些版本的语义是什么,所以我不会猜测实现。

Maybe Int 替换为使用在问题域中有意义的名称的等效类型可能也很有用:

data Chunk = Imaginary | Known Int

newtype MayVersion = MkMayVersion [Chunk]

通过为 Chunk 实现 EqOrd 也有可能在 MayVersion 上推导它们。

对于极其随意的版本控制概念(例如,可能涉及“@”或“.”以外的其他分隔符的版本),versions 包提供了一种有趣的版本类型,称为 Complex Mess这可能就足够了。不过,它只会以临时方式支持 "imaginary" 版本块。

我还应该提到,将 LTR 和 RTL cons 单元组合到同一个列表结构中似乎在构造上是不正确的,所以我没有考虑它。

是什么意思
VNL Nothing (VNR Nothing (VC (Just 1))

是吗?同时支持 RTL 和 LTR 的版本对我来说似乎无效。如果你想两者都有一个数据结构,你可以组合

data Version
  = LTR LTRVersion
  | RTL RTLVersion

通过构造确保两种版本类型都是正确的(当然,不会阻止无限版本号,但除此之外)。但是,我不清楚 LTR 版本和 RTL 版本应该如何相互比较,所以我不确定这是否真的需要。