如何在Haskell的子类定义中定义默认实现?
我是Haskell的新来者,以下是我的问题: 鉴于这一类别:如何在Haskell的子类定义中定义默认实现?,haskell,Haskell,我是Haskell的新来者,以下是我的问题: 鉴于这一类别: class MyClass a where foo :: a -> [a] 然后我有一个更具体的子类: class (MyClass a) => SubClass a where foo param = [bar param] bar :: a -> a 但它并没有像预期的那样起作用。我希望在子类的定义中设置一个默认实现,但它没有。我需要分别定义MyClass的实例,但这听起来很愚蠢。当我
class MyClass a where
foo :: a -> [a]
然后我有一个更具体的子类:
class (MyClass a) => SubClass a where
foo param = [bar param]
bar :: a -> a
但它并没有像预期的那样起作用。我希望在子类的定义中设置一个默认实现,但它没有。我需要分别定义MyClass
的实例,但这听起来很愚蠢。当我知道某个子类确实满足某个属性时,如何实现默认实现
更具体地说,我想用Haskell表示,当一个类满足某些属性时,它的父类的某些函数可以有默认实现。在我的示例中,SubClass
具有属性bar
,因此我知道foo
肯定是以这种方式定义的
这个问题的一个更一般的形式是,使用类和实例进行重用是一个好主意吗
我发现这个帖子:
这很接近,但仍然没有完全回答我的问题,而且它们的形式略有不同。这可以通过DefaultSignatures
实现:
{-# LANGUAGE DefaultSignatures #-}
class MyClass a where
foo :: a -> [a]
default foo :: SubClass a => a -> [a]
foo param = [param]
class MyClass a => SubClass a where
bar :: a -> a
在ghci中测试它:
> instance MyClass Integer; instance SubClass Integer where bar = id
> foo 3
[3]
详细说明丹尼尔的回答:
假设您有一个新的数据类型MyData
,定义为
data MyData = D1 | D2
您想使MyData
成为子类的一个实例。你先试试显而易见的解决办法
instance SubClass MyData where
bar x = case x of { D1 -> D2 ; D2 -> D1 }
不过,对类型签名的快速检查表明,这是行不通的,因为类型必须是MyClass
的实例,才能成为子类的实例。因此,您将MyData
作为MyClass
的一个实例
instance MyClass MyData
同样,这会引起一个错误,因为foo
包含在MyClass
的最小完整定义中。为了使您的实例工作,您必须手动定义foo
,因此无法达到默认声明的目的
简言之,在basic haskell 98(或haskell 2010)中无法做到这一点。不过,值得庆幸的是,GHC提供了一个名为DefaultSignatures
的有用扩展。以丹尼尔为例:
{-# LANGUAGE DefaultSignatures #-}
class MyClass a where
foo :: a -> [a]
default foo :: SubClass a => a -> [a]
foo param = [param]
class MyClass a => SubClass a where
bar :: a -> a
现在,您可以定义实例,它们将按照预期工作。此解决方案的缺点是必须在MyClass
中定义默认定义,但这是必需的。foo
的定义属于MyClass
的定义(或其一个实例声明),因此如果您能够在子类的定义中定义foo
的默认声明,Haskell的类型隔离将被打破 foo
的实现需要附加到foo
所属的类。如果MyClass
的第二个子类也试图像这样定义foo
的默认实现,您会期望发生什么?@ReidBarton我想表达的是,foo的实现在MyClass上是未知的,但鉴于子类的某些属性,可以决定foo的默认实现
。然后查看Daniel Wagner的答案,但请注意,在定义MyClass
@ReidBarton yeah时,必须选择子类子类
是什么。我读过。这不是最可取的。难道不能定义一个带有约束的实例来处理这个问题吗?这很好,但我希望默认实现出现在MyClass
中。通过定义一个带有约束的实例可以实现吗?比如instance(子类a)=>MyClass a
,但它看起来很奇怪。@hustmphrr我不确定我是否理解你的反对意见。你能进一步澄清吗?特别是,您说您希望默认实现出现在MyClass
中;但事实上它确实出现在那里。有什么问题吗?噢,我的脑子糟透了。我的意思是出现在子类
中,因为默认实现属于子类
@hustmphrr假设在您提议的世界中,我编写了类MyClass a=>子类1A,其中{foo x=[x]};类MyClass a=>子类2A,其中{foo x=[x,x]};实例子类1整数;实例子类2整数
。在那个世界上,[3]
或[3,3]
的意思是什么?我明白你的意思。但是默认签名不会有同样的问题吗?谢谢你的详细说明。现在很清楚了。但是,为所有子类定义MyClass
的实例怎么样?类似于实例(子类a)=>MyClass a
?是什么阻止了我这么做?@hustmphrr:这样的声明将无法通过歧义检查。只有当占位符的类型可以从上下文推断时,带有不明确类型声明的类型声明(具有a
等类型的声明)才起作用。例如,如果您有一个签名为[a]->Int
的函数,然后用['c'、'$'、'\n']
调用它,它可以推断a
的类型为Char
,因此成功。在您的示例中,没有上下文,因此无法确定a
的类型。这与类型为[a]
的列表必须仍然是同构的原因相同。