我怎么写,“如果typestypesa,那么a也是这个定义的b的一个实例。”

我有一个types类MyClass ,并有一个函数,它会产生一个String 。 我想用这个来暗示Show一个实例,以便我可以传递实现MyClasstypes来show 。 到目前为止,我有,

 class MyClass a where someFunc :: a -> a myShow :: a -> String instance MyClass a => Show a where show a = myShow a 

这给出了错误Constraint is no smaller than the instance head. 我也试过了,

 class MyClass a where someFunc :: a -> a myShow :: a -> String instance Show (MyClass a) where show a = myShow a 

这给出错误, Class MyClass'用作types`。

我怎样才能正确expression这种关系在Haskell? 谢谢。

我应该补充一点,我希望通过MyClass特定实例,根据它们的types发出特定的string。 例如,

 data Foo = Foo data Bar = Bar instance MyClass Foo where myShow a = "foo" instance MyClass Bar where myShow a = "bar" main = do print Foo print Bar 

(编辑:留下后人的尸体,但跳到最后的真正解决scheme)

在声明instance MyClass a => Show a ,让我们检查一下错误“约束不小于实例头”。 约束是'=>'左侧的types约束,本例中为MyClass a 。 “实例头”是你正在写一个实例的类之后的所有东西,在本例中a (在Show的右边)。 GHC中的一个types推理规则要求约束的构造器和variablesless于头部。 这是所谓的“ 帕特森条件 ”的一部分。 这些作为types检查终止的保证。

在这种情况下,约束与头部完全相同,即a ,所以它不能通过这个testing。 您可以通过启用UndecidableInstances删除Paterson条件检查,最有可能使用{-# LANGUAGE UndecidableInstances #-}附注。

在这种情况下,您基本上将您的类MyClass用作Show类的types类别同义词。 像这样创build类的同义词是UndecidableInstances扩展的规范用法之一,所以你可以在这里安全地使用它。

“Undecidable”意味着GHC无法certificatetypes检测将终止。 尽pipe听起来很危险,但启用UndecidableInstances可能发生的最糟糕的情况是编译器会循环,最终在耗尽堆栈之后终止。 如果编译,那么明显的types检查终止,所以没有问题。 危险的扩展是IncoherentInstances,这听起来很糟糕。

编辑:通过这种方法可能产生的另一个问题来自这种情况:

 instance MyClass a => Show a where data MyFoo = MyFoo ... deriving (Show) instance MyClass MyFoo where 

现在有两个Show for MyFoo实例,一个来自派生子句,另一个来自MyClass实例。 编译器不能决定使用哪一个,所以它会出现一个错误信息。 如果你正在试图创buildMyClasstypes的实例,那么你不能控制已经有了Show实例,那么你将不得不使用newtypes来隐藏已经存在的Show实例。 即使没有MyClass实例的types仍然会发生冲突,因为定义instance MyClass => Show a因为定义实际上为所有可能的a提供了一个实现(上下文检查在后面出现;它不涉及实例select)

所以这是错误信息,以及UndecidableInstances如何让它消失。 不幸的是,在实际的代码中使用起来很麻烦,Edward Kmett解释说。 最初的动力是避免在已经有一个MyClass约束时指定一个Show约束。 鉴于此,我会做的只是从MyClass而不是show使用myShow 。 你根本不需要Show约束。

我希望强烈不同意迄今为止所提出的破解。

 instance MyClass a => Show a where show a = myShow a 

由于实例parsing工作的方式,这是一个非常危险的实例运行!

实例parsing是通过在每个实例的右侧进行有效的模式匹配来完成的,完全不考虑=>左边的内容。

当这些实例没有重叠时,这是一件美丽的事情。 然而,你在这里说的是“这是一个规则,你应该使用每个 Show实例当问任何types的展示实例,你需要一个MyClass的实例,所以去得到,这是实现“。 – 一旦编译器已经承诺select使用你的实例,(只是凭借“a”与所有事物相结合的事实),它就没有机会回退并使用任何其他实例!

如果你打开{-# LANGUAGE OverlappingInstances, IncoherentInstances #-}等来编译,当你去编写导入提供这个定义的模块的模块时,你会得到不那么微妙的失败,并且需要使用任何其他Show实例。 最终,你将能够得到这个代码编译足够的扩展,但可悲的是不会做你认为它应该做的!

如果你考虑一下:

 instance MyClass a => Show a where show = myShow instance HisClass a => Show a where show = hisShow 

编译器应该select哪个?

你的模块可能只定义其中的一个,但最终用户代码将导入一堆模块,而不只是你的。 另外,如果另一个模块定义

 instance Show HisDataTypeThatHasNeverHeardOfMyClass 

编译器完全有权忽略他的实例并尝试使用你的实例。

可悲的是,正确的答案是做两件事。

对于MyClass的每个单独的实例,您可以用非常机械的定义来定义一个相应的Show实例

 instance MyClass Foo where ... instance Show Foo where show = myShow 

这是相当不幸的,但只有less数MyClass正在考虑的情况下运行良好。

当你有大量的实例时,避免代码重复的方法(当类比显示复杂得多时)就是定义。

 newtype WrappedMyClass a = WrapMyClass { unwrapMyClass :: a } instance MyClass a => Show (WrappedMyClass a) where show (WrapMyClass a) = myShow a 

这提供了作为例子调度的车辆的新types。 接着

 instance Foo a => Show (WrappedFoo a) where ... instance Bar a => Show (WrappedBar a) where ... 

是明确的,因为WrappedFoo aWrappedBar a的types“模式”是不相交的。

这个成语在base包里有很多例子。

在Control.Applicative中有WrappedMonadWrappedArrow定义。

理想情况下,你可以说:

 instance Monad t => Applicative t where pure = return (<*>) = ap 

但实际上这个实例所说的是,每个应用程序都应该首先为Monadfind一个实例,然后派发给它。 因此,虽然它有意图说每个Monad都是适用的(顺便说一句,类似于=>读取),但实际上每个应用程序都是Monad,因为实例头“t”匹配任何types。 在很多方面,“实例”和“类”定义的语法是倒退的。

我认为这样做会更好:

 class Show a => MyClass a where someFunc :: a -> a myShow :: MyClass a => a -> String myShow = show 

你可以编译它,但不能用Haskell 98,你必须启用一些语言扩展:

 {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} -- at the top of your file 

灵活的实例是允许上下文在实例声明。 我真的不知道UndecidableInstances的含义,但我会尽可能地避免。

您可能会在相关的SO问题中find一些有趣的答案: 在Haskell中链接/组合types类

正如Ed Kmett所指出的那样,你的情况根本不可能。 但是,如果您有权访问要为其提供默认实例的类,则可以使用默认实现将该样板减至最小,并使用您需要的默认签名来限制inputtypes:

 {-# LANGUAGE DefaultSignatures #-} class MyClass a where someFunc :: a -> Int class MyShow a where myShow :: a -> String default myShow :: MyClass a => a -> String myShow = show . someFunc instance MyClass Int where someFunc i = i instance MyShow Int main = putStrLn (myShow 5) 

请注意,唯一真正的样板(除了整个例子)减less到instance MyShow Int

请参阅ToJSON以获得更实际的示例。