你可以在类型类约束参数上模式匹配构造函数吗?

时间:2011-04-22 17:52:04

标签: haskell pattern-matching typeclass

请参阅下面的代码示例。它不会编译。我原以为可能是因为它必须为测试函数中的第一个参数设置一个类型。但这没有意义,因为如果我没有对它进行模式匹配以便编译,我可以用MyObj11 5MyObj21 5来调用它,它们是两种不同的类型。

那么是什么限制所以你不能在构造函数上使用类型类约束参数进行模式匹配?或者你有什么机制可以吗?

class SomeClass a where toString :: a -> String

instance SomeClass MyType1 where toString v = "MyType1"
instance SomeClass MyType2 where toString v = "MyType2"

data MyType1 = MyObj11 Int | MyObj12 Int Int 
data MyType2 = MyObj21 Int | MyObj22 Int Int 

test :: SomeClass a => a -> String
test (MyObj11 x) = "11"
test (MyObj12 x y) = "12" -- Error here if remove 3rd line: rigid type bound error
test (MyObj22 x y) = "22" -- Error here about not match MyType1.

1 个答案:

答案 0 :(得分:19)

  

是什么限制所以你不能在构造函数上使用类型类约束参数进行模式匹配?

在显式构造函数上进行模式匹配时,您将提交特定的数据类型表示。此类数据类型不会在类的所有实例之间共享,因此无法以这种方式编写适用于所有实例的函数。

相反,您需要将所需的不同行为与每个实例相关联,如下所示:

class C a where 
    toString   :: a -> String
    draw       :: a -> String

instance C MyType1 where
    toString v = "MyType1"

    draw (MyObj11 x)   = "11"  
    draw (MyObj12 x y) = "12"

instance C MyType2 where
    toString v = "MyType2"

    draw (MyObj22 x y) = "22"

data MyType1 = MyObj11 Int | MyObj12 Int Int 
data MyType2 = MyObj21 Int | MyObj22 Int Int 

test :: C a => a -> String
test x = draw x

原始test函数的分支现在分布在实例中。

一些替代技巧涉及使用class-associated data types(向编译器证明数据类型在所有实例之间共享),或view patterns(允许您概括模式匹配)。


查看模式

我们可以使用视图模式来清除模式匹配和类型类实例之间的连接,允许我们通过共享类型上的模式匹配来近似实例之间的模式匹配。

这是一个例子,我们编写一个函数,有两个案例,它们允许我们模式匹配类中的任何东西。

{-# LANGUAGE ViewPatterns #-}

class C a where 
    view       :: a -> View

data View = One Int
          | Two Int Int

data MyType1 = MyObj11 Int | MyObj12 Int Int 

instance C MyType1 where
    view (MyObj11 n) = One n
    view (MyObj12 n m) = Two n m

data MyType2 = MyObj21 Int | MyObj22 Int Int 

instance C MyType2 where
    view (MyObj21 n)   = One n
    view (MyObj22 n m) = Two n m

test :: C a => a -> String
test (view -> One n)   = "One " ++ show n
test (view -> Two n m) = "Two " ++ show n ++ show m

请注意->语法如何让我们在每个实例中回调正确的view函数,查找每种类型的自定义数据类型编码,以便对其进行模式匹配。

设计挑战是提出一种视图类型,捕获您感兴趣的所有行为变体。

在您的原始问题中,您希望每个构造函数都有不同的行为,因此实际上没有理由使用视图类型(在每个实例中直接调度该行为已经足够好了。)