我是Haskell的新手并创建了这个类
class Printable a where
toString :: a -> String
其中两个实例
instance Printable Bool where
toString x | x == True = "true"
| x == False = "false"
instance Printable () where
toString x = "unit type"
它们工作得很好,现在我想创建一个带两个参数的实例,但是我遇到了麻烦。我到目前为止创造的是
instance (Printable a, Printable b) => Printable (a, b) where
toString x | fst x == True = "(true,unit type)"
但现在我收到了错误
无法将预期类型'a'与实际类型'Bool'匹配
这是合理的,但当我用a
替换Bool
时,我也会收到错误消息。我怎样才能让它首先发挥作用,我是否可以限制a
和b
的类型 - 例如,如果我希望a
始终为Bool
该怎么办?和b
永远是()?
答案 0 :(得分:8)
您编写了一个实例,其中您为元组(a, b)
编写了该元组,如果项Printable
和a
为b
,则元组为Printable
。但不意味着a
或b
是Bool
s或单位。
您可能认为 - 就像在许多语言中一样 - (==)
运算符适用于任何两种类型,但在Haskell中(==)
具有类型(==) :: Eq a => a -> a -> Bool
,所以它是左边的函数右操作数具有相同的类型。因此,我们无法使用x == True
,除非x
是Bool
。
我们不需要这些警卫。我们需要做的是调用操作数的toString
函数,所以:
instance (Printable a, Printable b) => Printable (a, b) where
toString (x,y) = "(" ++ toString x ++ "," ++ toString y ++ ")"
我们也可以将[x] ++
替换为x : ...
,以便我们将其重写为:
instance (Printable a, Printable b) => Printable (a, b) where
toString (x,y) = '(' : toString x ++ ',' : toString y ++ ")"
编辑:如果您希望a
始终为Bool
,您可以写信:
instance Printable b => Printable (Bool, b) where
toString (x,y) = '(' : toString x ++ ',' : toString y ++ ")"
您当然可以添加警卫,模式匹配或其他方法来区分x == True
和x == False
,但我建议您不要这样做,因为这违反了DRY原则:< strong> D on R epeat Y 我们自己。
答案 1 :(得分:4)
仅仅因为a
有Printable
个实例并不意味着您可以假设a ~ Bool
,所以x == True
会失败。
但是,您应该使用模式匹配而不是相等,这样可以回避问题。
class Printable a where
toString :: a -> String
instance Printable Bool where
toString True = "true"
toString False = "false"
instance Printable () where
toString () = "unit type"
然后,您的元组实例应该使用a
和b
都是Printable
的事实。您只需要在构造函数上进行模式匹配,而不是单个组件。
instance (Printable a, Printable b) => Printable (a, b) where
toString (x, y) = "(" ++ toString x ++ "," ++ toString y ++ ")"