问题描述
我想写一些类似于:
$ b
{ - #LANGUAGE FlexibleContexts,FlexibleInstances# - }
import Data.ByteString.Char8(ByteString,pack)
import Data.Foldable(可折叠)
class(显示a)=>行a where
rRepr :: a - > [ByteString]
rRepr =(:[])。包。显示
实例(可折叠f,显示(f a))=>行(fa)其中
rRrr = const []
表示 fa
实例化行
如果 f
实例化可折叠
和 fa
实例化显示
。当我运行ghc时,我得到:
约束不小于约束中的实例头部
:Show(fa )
(使用-XUndecidableInstances来允许这样做)
在'行(fa)'的实例声明中
我有两个问题:
- 在错误中小意味着什么?问题是什么? li>
- 在不使用
UndecidableInstances
?
让我们玩编译器:我们有一个类型(fa)
它是 Rows
约束的有效满足者。为此,我们需要派发(f a)
满足 Show(f a)
的证明。这不是一个问题,除非有人写了一个
实例Rows(f a)=>显示(f a)其中...
在这种情况下,我回到了我开始的地方。像这样编码一个无限循环是愚蠢的,但Haskell静态地确保它实际上是不可能的,除非你要求 UndecidableInstances
。
通常,Haskell确保解析器追踪的每个步骤至少通过1个构造函数来减少类型的大小。这导致了一个非常简单的结构归纳证明,我们最终将得到一个在有限数量的分辨率下的裸类型。
这是过分限制的,显然有些实例即使不立即减少构造树,分辨率步骤也是有意义的,有用的和完整的。这种类型的总体限制适用于Agda和Coq等语言,并且通常将您的算法操作为通过结构化归纳进行的算法,这往往是一种说明性挑战。
那么我们该如何解决?一种方法是在类
定义中使用像 Show1这样的实例丢失
显示
约束。 / code> from 的。
class Show1 f where ...
show1 ::(Show1 f,Show a)=> f a - >字符串 - 不是实例定义!
然后有实例(Foldable f,Show1 f,Show a)= >行(f a)其中...
它在我的测试中起作用。您可以将默认实例编写为独立函数。
defRRepr =(:[])。包。显示
并且在写一个 另一种解决方案是使用 I would like to write something like: meaning that I have two questions:的实例定义时使用它显示$
newtype $ c
instance(Foldable f,Show)使用$ c> wrappers来允许Haskell看到一个图层 (fa))=>行(FoldableRow f a)其中
rRepr = const []。 unFR
newtype FoldableRow f a = FR {unFR :: f a}派生(显示)
{-# LANGUAGE FlexibleContexts,FlexibleInstances #-}
import Data.ByteString.Char8 (ByteString,pack)
import Data.Foldable (Foldable)
class (Show a) => Rows a where
rRepr :: a -> [ByteString]
rRepr = (:[]) . pack . show
instance (Foldable f,Show (f a)) => Rows (f a) where
rRepr = const []
f a
instantiate Rows
if f
instantiate Foldable
and f a
instantiate Show
. When I run ghc I get:Constraint is no smaller than the instance head
in the constraint: Show (f a)
(Use -XUndecidableInstances to permit this)
In the instance declaration for `Rows (f a)'
UndecidableInstances
?
Let's play compiler: we have a type (f a)
we'd like to see if it is a valid satisfier of a Rows
constraint. To do so, we need to dispatch a proof that (f a)
satisfies Show (f a)
as well. This isn't a problem unless someone writes an
instance Rows (f a) => Show (f a) where ...
in which case I'm back where I began. Encoding an infinite loop like this is sort of foolish, but Haskell statically ensures that it's actually impossible unless you ask for UndecidableInstances
.
Normally Haskell ensures that each step of a "resolution chase" reduces the size of the type by at least 1 constructor. This leads to a really simple proof by structural induction that we'll end up with a bare type in a finite number of resolutions.
This is overly restrictive, clearly some instance resolution steps are meaningful, useful, and total even if they don't immediately reduce the constructor tree. This same kind of totality restriction applies in languages like Agda and Coq and often it's a bit of an illustrative challenge to manipulate your algorithm into one which proceeds by structural induction.
So how can we fix it? One way is to lose the Show
constraint on the class
definition use an instance like Show1
from prelude-extras.
class Show1 f where ...
show1 :: (Show1 f, Show a) => f a -> String -- not an instance definition!
and then have instance (Foldable f, Show1 f, Show a) => Rows (f a) where ...
which works in my testing. You can then write a default instance as a standalone function.
defRRepr :: Show a => a -> [ByteString]
defRRepr = (:[]) . pack . show
and use it whenever writing an instance definition for a Show
able thing.
The other solution is to use newtype
wrappers to allow Haskell to see that a "layer" of resolution has been removed.
instance (Foldable f, Show (f a)) => Rows (FoldableRow f a) where
rRepr = const [] . unFR
newtype FoldableRow f a = FR { unFR :: f a } deriving (Show)
这篇关于什么“Contraint不比实例头部更小”意味着如何解决它的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!