我怎么写,“如果typestypesa,那么a也是这个定义的b的一个实例。”
我有一个types类MyClass
,并有一个函数,它会产生一个String
。 我想用这个来暗示Show
一个实例,以便我可以传递实现MyClass
types来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实例。 编译器不能决定使用哪一个,所以它会出现一个错误信息。 如果你正在试图创buildMyClass
types的实例,那么你不能控制已经有了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 a
和WrappedBar a
的types“模式”是不相交的。
这个成语在base
包里有很多例子。
在Control.Applicative中有WrappedMonad
和WrappedArrow
定义。
理想情况下,你可以说:
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
以获得更实际的示例。