【问题标题】:Match against type in Haskell typeclass匹配 Haskell 类型类中的类型
【发布时间】:2017-06-03 23:47:09
【问题描述】:

我不知道如何准确地表达这个,所以如果有人有一个好名字,请告诉我。

我正在尝试编写一个名为Matchable 的类型类。这个想法是我拥有的几种不同类型的正则表达式(RegExp aComplexRegex a)应该能够匹配输入。

所以我尝试了这个:

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

但我真正想要的是用类型构造函数变量或其他东西解构类型类中的构造函数:

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

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

【问题讨论】:

  • 不完全清楚你想要什么。您能否为您想要抽象的match 的不同情况提供一些类型签名?例如matchRegExp :: RegExp Char -> [Char] -> BoolmatchComplexReg :: ComplexRegex ...
  • 您可能需要关联类型(较新、更详细)或fundeps(较旧、更简洁),但如果没有实际代码就很难说
  • @jberryman 和 haoformayor 看到 Alec 的回答——差不多就是这样

标签: haskell typeclass


【解决方案1】:

为了简单起见,为什么不把Matchable 的类变量设置成* -> * 呢? (参见FunctorApplicativeMonadFoldableTraversable 了解其他更高年级课程的示例)。

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

instance Matchable ComplexRegex where
  -- match :: ComplexRegex a -> [a] -> Bool
  match = error "unimplemented"

instance Matchable RegExp where
  -- match :: RegExp a -> [a] -> Bool
  match = error "unimplemented"

然后,您可以将String ~ [Char]ComplexRegex CharRegExp Char 匹配。

【讨论】:

  • 注:您可能至少需要(Eq a) => b a -> [a] -> Bool,否则matcha 中没有任何可使用的结构。
  • 这太棒了——谢谢!有没有办法将类型约束 Eq a 放在类中,而不是函数 match
  • @tokknoiagi,我想你可以在类中添加一个方法matchableEq :: b a -> Dict (Eq a)(其中Dict来自constraints),它表示如果bMatchable,那么@987654342 @ 总是带有 Eq a 字典。但这将是非常非正统的设计(虽然玩起来很有趣)。 match :: (Eq a) => ... 似乎完全合理。
  • 谢谢!我同意;这看起来很干净。
【解决方案2】:

作为 Alec 解决方案的替代方案,您可以使用 associated types。这允许没有类型* -> * 的类型,但如果没有必要,我会选择更简单的解决方案。例如。想象一下,除了RegExp aComplexRegex a,你还有StringRegex 没有参数化,或者你只能为ComplexRegex Char 实现match 而不是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 ...)。

【讨论】:

  • 我认为这个答案可能会受益于另一个实例示例,例如instance Matchable StringRegex where { type Target StringRegex = Char ... }
  • @Alec 是的,我最初忽略了它。
  • 另一个小细节:这种方法可以让您对每个实例的 Target 类型有不同的约束 - 而不仅仅是在类上。
【解决方案3】:

您可能正在寻找更高种类的类型。如果您只对CharString 感兴趣并且函数的行为仅取决于它是RegExp 还是ComplexRegex,那么您可以定义下一个类型类:

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 语言扩展很有帮助。

【讨论】:

  • 这是一个奇怪的类。此时仅使用简单的match :: r -> String -> Bool 不会损失表达性,因为类型参数没有以多态方式使用。
  • @luqui 我同意拥有match :: r a -> [a] -> Bool 函数作为一个更多态的变体可能更有用。但对于初学者来说,如果这种类型的解决方案被接受,那么从较少多态的版本开始可能会更容易。
  • @Shersh 为什么不一直使用match :: r -> String -> Bool 并打开FlexibleInstances?似乎这样,如果有的话,会更简单。
  • @Alec 如果您只关心Char,那么match :: r -> String -> Bool 可能是一个合理的选择。我无法争辩哪种方法更好,只是尽可能避免一些扩展:) 但是r Char 离更多态的版本更近了一步,这最终将是正确的选择。答案是关于解决问题的高级类型。
猜你喜欢
  • 2014-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多