alt*_*ern -2 haskell version-numbering
我正在尝试为替代版本编号方法定义Eq运算符.
type VersionCompound = Maybe Int -- x, 0, 1, 2, ...
type VersionNumber = [VersionCompound] -- x.x, x.0, x.1, x.2, ... , 1.0, 1.1, 1.2, ... 1.x.x, 2.x.x, 3.x.x, ...
instance Eq VersionNumber where
[] == [] = True
(x:[]) == (y:[]) = x == y
(Nothing:xs) == ys = (xs == ys)
xs == (Nothing:ys) = (xs == ys)
Run Code Online (Sandbox Code Playgroud)
据预计,它返回True了以下情况:x.x.x == x,1.x.x == x.1.x.x,x.1 == 1等,但相反,它会返回一个错误:
VersionNumber.hs:58:34:
Overlapping instances for Eq [VersionCompound]
arising from a use of ‘==’
Matching instances:
instance Eq a => Eq [a] -- Defined in ‘GHC.Classes’
instance Eq VersionNumber -- Defined at VersionNumber.hs:55:10
In the expression: (xs == ys)
In an equation for ‘==’: (Nothing : xs) == ys = (xs == ys)
In the instance declaration for ‘Eq VersionNumber’
Run Code Online (Sandbox Code Playgroud)
任何想法如何解决它?
编辑:我通过列表上的模式匹配来解决这个问题的方法结果是不完整的.我想忽略给定版本左侧的任意列表x(或Nothings).因此,例如,x.x.x.x.x将等于x.x.x和x.同样,x.x.x.1也将等于x.x.1和1.如果x中间有一个,它将不会被丢弃.因此,对于这种情况,x.x.1.x.0将等于x.1.x.0和1.x.0.又一个例子:x.1.x.x.0.x等于1.x.x.0.x并x.1.x.0.x等于1.x.0.x(你只需删除x左侧的's).
修复错误后我正在努力解决的问题Overlapping instances for Eq [VersionCompound]是如何获得x.x.x == x- > True模式匹配.但是,正如@WillemVanOnsem出色地指出的那样,它不应该通过模式匹配来实现,而应该通过功能组合来实现.
PS.我个人鼓励你通过@WillemVanOnsem来回答这个问题,因为他的解决方案非常优雅,需要付出一些努力来代表Haskell功能的本质.
您使用类型别名.这意味着您尚未定义单独的类型VersionNumber或VersionCompound; 你只是构造了一个别名.在窗帘后面,Haskell看得很VersionNumber简单[Maybe Int].
现在,如果我们查看Haskell的库,我们会看到:
instance Eq Int where
-- ...
instance Eq a => Eq (Maybe a) where
-- ...
instance Eq a => Eq [a] where
-- ...
Run Code Online (Sandbox Code Playgroud)
所以这意味着Eq Int定义,Eq (Maybe Int)也定义,因此也Eq [Maybe Int]由Haskell库定义.所以你实际上已经构建了一个Eq VersionNumber没有写一个.现在你试着写一个额外的,当然,编译器会混淆选择哪一个.
有一些方法可以解决重叠实例,但这可能只会产生更多麻烦.
因此,您最好data使用单个构造函数构造一个类型.例如:
type VersionCompound = Maybe Int
data VersionNumber = VersionNumber [VersionCompound]Run Code Online (Sandbox Code Playgroud)
现在,因为只有一个构造函数,所以最好使用newtype:
type VersionCompound = Maybe Int
newtype VersionNumber = VersionNumber [VersionCompound]Run Code Online (Sandbox Code Playgroud)
现在我们可以定义我们的特殊实例,如:
instance Eq VersionNumber where
(VersionNumber a) == (VersionNumber b) = a =*= b
where [] =*= [] = True
(x:[]) =*= (y:[]) = x == y
(Nothing:xs) =*= ys = (xs =*= ys)
xs =*= (Nothing:ys) = (xs =*= ys)Run Code Online (Sandbox Code Playgroud)
因此,我们解开构造函数,然后使用另一个本地定义的函数=*=,其功能与您在问题中定义的函数类似.
但请注意,您在程序中忘记了一些模式,例如Just x : xs在左侧和右侧.所以,你最好先解决这些问题.如果我通过编译器运行代码,我会收到以下警告:
Pattern match(es) are non-exhaustive
In an equation for ‘=*=’:
Patterns not matched:
[] (Just _:_)
[Just _] []
[Just _] (Just _:_:_)
(Just _:_:_) []
Run Code Online (Sandbox Code Playgroud)
你想如何处理这些案件当然取决于你.@DanielWagner建议使用:
import Data.Function(on)
import Data.Maybe(catMaybes)
instance Eq VersionNumber where
(VersionNumber a) == (VersionNumber b) = on (==) catMaybes a b
Run Code Online (Sandbox Code Playgroud)
这将过滤掉Nothing两个VersionNumbers 的值,然后检查Just生成相同列表中的值.所以3.x.2.x.x.1将等于3.2.1和x.x.x.x.x.3.2.1.
编辑:根据您在评论中的规范,您可能正在寻找:
import Data.Function(on)
instance Eq VersionNumber where
(VersionNumber a) == (VersionNumber b) = on (==) (dropWhile (Nothing ==)) a b
Run Code Online (Sandbox Code Playgroud)