2017-06-02 57 views
2

我不确定如何正确地使用这个词,如果有人对它有很好的名称,请让我知道。与Haskell typeclass中的类型匹配

我想写一个叫做Matchable的类型类。这个想法是我有几种不同类型的正则表达式(RegExp a,ComplexRegex a)应该能够匹配输入。

所以,我想这一点:

class Matchable a where 
    -- regex, input, success 
    match :: a -> b -> Bool 

但我真正想要的是解构的构造与类型构造变量什么的类型类:

class Matchable a where 
    -- regex, input, success 
    match :: (B a) -> [a] -> Bool 

这样我可以有RegExp CharComplexRegex Char两者都匹配在String上。有没有办法做到这一点?谢谢。

+0

这并不完全清楚你想要什么。你可以提供一些类型的签名,用于你想抽象的不同情况下的“匹配”吗?例如'matchRegExp :: RegExp Char - > [Char] - > Bool','matchComplexReg :: ComplexRegex ...'等 – jberryman

+0

您可能想要关联类型(更新,更详细)或fundeps(更老,更不详细),但它是很难说没有实际的代码 – hao

+0

@jberryman和haoformayor看到亚历克的答案 - 几乎完全是 – tekknolagi

回答

6

保持简单,为什么不让Matchable的类变量有种类* -> *? (请参阅Functor,Applicative,Monad,Foldable,Traversable查看更高幼儿班的其他例子)。

然后,您可以匹配针对String ~ [Char]与无论是ComplexRegex CharRegExp Char

+1

NB你可能至少需要'(Eq a)=> b a - > [a] - > Bool',否则'match'没有任何结构可以在'a'中使用。 – luqui

+0

这很棒 - 谢谢!有没有办法将类型约束'Eq a'放在类中,而不是函数'match'? – tekknolagi

+1

@tokknoiagi,我想你可以添加一个方法'matchableEq :: ba - > Dict(Eq a)'给类(其中'Dict'来自[constraints](https://hackage.haskell.org/package/) constraint-0.9.1/docs/Data-Constraint.html#t:Dict)),它表示如果'b'是'Matchable',那么'ba'总是带有'Eq a'字典。但是,这将是非常不寻常的设计(尽管玩起来很有趣)。 'match ::(Eq a)=> ...'似乎完全合理。 – luqui

1

您可能正在寻找更高级的类型。如果你只关心CharString和功能行为只取决于它是否RegExpComplexRegex,那么你可以定义下一个类型的类:

class Matchable r where 
    -- regex, input, success 
    match :: r Char -> String -> Bool 

这里r是键入有一种* -> *变量。简而言之,这是一个不完整类型。像Maybe。你的函数不能有Maybe -> Int类型,因为Maybe是不完整类型,但是Maybe Bool是完整类型。与正则表达式类似:RegExpComplexRegex都是不完整的类型。由于Haskell伸展类型系统,您的功能也可以通过不完整类型进行参数化。所以以后当你定义的情况下,你将它们写在接下来的方式:

instance Matchable RegExp where 
    ... -- implementation for RegExp goes here 

instance Matchable ComplexRegex where 
    ... -- implementation for CompltexRegex goes here 

指定的功能类型和类型的类型时,明确你可以找到-XKindSignatures-XInstanceSigs语言扩展很有帮助。

+0

这是一个奇怪的类。在这一点上仅仅使用简单的'match :: r - > String - > Bool'就不会损失表达性,因为type参数没有以多态的方式被使用。 – luqui

+0

@luqui我同意'match :: r a - > [a] - > Bool'函数可能更有用,因为它是一个更多变体的变体。但是,对于初学者来说,如果这种类型的解决方案将被接受,那么以较少的多态版本开始更容易。 – Shersh

+0

@Shersh为什么不能一直走,并有'match :: r - >字符串 - > Bool',并打开'FlexibleInstances'?似乎是这样,如果有的话,会更简单。 – Alec

2

作为亚历克解决方案的替代方案,您可以使用associated types。这允许类型没有类型* -> *,但如果没有必要,我会采用更简单的解决方案。例如。想象一下,除了RegExp aComplexRegex a你有StringRegex有没有参数化的,或者你只能实现matchComplexRegex Char,而不是ComplexRegex a

class Matchable b where 
    type Target b :: * 
    -- regex, input, success 
    match :: b -> [Target b] -> Bool 

instance Eq a => Matchable (ComplexRegex a) where 
    type Target (ComplexRegex a) = a 
    -- match :: ComplexRegex a -> [a] -> Bool 
    match = error "unimplemented" 

instance Matchable StringRegex where 
    type Target StringRegex = Char 
    -- match :: StringRegex -> [Char] -> Bool 
    match = error "unimplemented" 

-- requires FlexibleInstances 
instance Matchable (ComplexRegex Char) where 
    type Target (ComplexRegex Char) = Char 
    -- match :: ComplexRegex Char -> [Char] -> Bool 
    match = error "unimplemented" 

另一个区别是,你的情况下,可以保证Eq (Target b),而不是把它转换为match的类型(并且您也可以将其作为要求:class Eq (Target b) => Matchable b where ...)。

+0

我认为这个答案可以从另一个实例中受益,例如'实例Matchable StringRegex where {type Target StringRegex = Char ...}'。 – Alec

+0

@Alec是的,我最初忽略了它。 –

+0

另一个小细节:这种方法可以让你对每个实例的'Target'类型有不同的约束,而不仅仅是在类上。 – Alec