为什么 Haskell 的实例定义中需要构造函数

发布于 2024-12-06 02:47:53 字数 210 浏览 3 评论 0原文

任何人都可以解释这个错误:

实例头中的语法错误(需要构造函数)

class Nullable v where
  default_val :: v


instance Num a => Nullable a where  -- error reported here
  default_val = 0::a

谢谢

Can anyone explain this error:

Syntax error in instance head (constructor expected)

class Nullable v where
  default_val :: v


instance Num a => Nullable a where  -- error reported here
  default_val = 0::a

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

荒路情人 2024-12-13 02:47:53

首先,hackage 可以满足您的需求

其次,不要编写

instance OtherClass a => Class a where

它们不能按您想要的方式工作的表单实例。 (YMMV 使用 GHC 以外的编译器,但即使如此我也不认为这是一个好主意)。大概您的意图是创建多个实例,例如:

instance Num a => Nullable a where  -- error reported here
  default_val = 0::a

instance Bounded a => Nullable a where
  default_val = minBound

您可以将类型类约束视为函数的额外参数,如下所示:

instance Num a => Nullable a where
  default_val = NumDict a -> 0::a

instance Bounded a => Nullable a where
  default_val = BoundedDict a -> minBound

然后 ghc 实际上会看到这些类实例,如下所示:

instance Nullable a where
  default_val = NumDict a -> 0::a

instance Nullable a where
  default_val = BoundedDict a -> minBound

现在,编译器应选择哪个实例?有两个实例都声称对所有类型都有效。所以出现了编译器错误。

即使您有一种基于类型类的实例,这也是一个问题。假设您有这些实例:

instance Num a => Nullable a where
  default_val = 0::a

instance Nullable String where
  default_val = ""

instance Nullable BS.ByteString where
  default_val = BS.empty

第一个仍然被认为对所有类型都有效,因此 ghc 表示它需要 OverlappingInstances 扩展才能使用它们。这并不完全是邪恶的。但是当您尝试使用它时,不久之后 ghc 将需要另一个扩展,IncoherentInstances

很多人害怕使用 UndecidableInstances,但这种担心是错误的。 UndecidableInstances 可能发生的最糟糕情况是编译不会终止(但通常会终止)。 IncoherentInstances 是一个应该会引起恐惧的扩展,因为它会给你的代码带来厄运。如果 GHC 说您必须启用 IncoherentInstances,则意味着您需要更改代码。

tl;dr

不要编写以下形式的实例

instance OtherClass a => Class a where

他们不做你想做的事。

First off, hackage has you covered.

Secondly, don't write instances of the form

instance OtherClass a => Class a where

they don't work like you want. (YMMV with compilers other than GHC, but I can't see that it's a good idea even then). Presumably your intent is to create several instances, something like:

instance Num a => Nullable a where  -- error reported here
  default_val = 0::a

instance Bounded a => Nullable a where
  default_val = minBound

You can think of type class constraints as an extra argument to a function, like this:

instance Num a => Nullable a where
  default_val = NumDict a -> 0::a

instance Bounded a => Nullable a where
  default_val = BoundedDict a -> minBound

Then ghc actually sees these class instances like this:

instance Nullable a where
  default_val = NumDict a -> 0::a

instance Nullable a where
  default_val = BoundedDict a -> minBound

Now, which instance should the compiler choose? There are two instances that both claim to be valid for all types. So there's a compiler error.

This is a problem even if you one have one type class based instance. Suppose that you have these instances:

instance Num a => Nullable a where
  default_val = 0::a

instance Nullable String where
  default_val = ""

instance Nullable BS.ByteString where
  default_val = BS.empty

The first is still considered valid for all types, so ghc says that it needs the OverlappingInstances extension to use them all. That's not entirely evil. But when you try to make any use of this, before long ghc will require another extension, IncoherentInstances.

Lots of people are afraid to use UndecidableInstances, but that fear is misplaced. The worst that can happen with UndecidableInstances is that compiling won't terminate (but it usually does). IncoherentInstances is the extension which should inspire fear, as it will bring doom upon your code. If GHC says that you have to enable IncoherentInstances, it means you need to change your code.

tl;dr

Don't write instances of the form

instance OtherClass a => Class a where

They don't do what you want.

沫离伤花 2024-12-13 02:47:53

Haskell 中有一条规则,约束必须“小于”实例本身。我不太明白它背后的理论依据,但实际上你可以用 newtype 来解决它。

newtype N a = N a

instance Num a => Nullable (N a) where
    default_val = N 0

GHC 还可以选择禁用其中一些规则。请参阅http://www.haskell.org/ ghc/docs/latest/html/users_guide/type-class-extensions.html

另请注意,您不能将 ::a 放在那里。它在实例声明中并不引用相同的 a,但被视为通用 a,这在那里没有任何意义。

There is a rule in Haskell that the constraints must be "smaller" than the instance itself. I don't exactly understand the theoretical justification behind it, but as a practical matter you can get around it with a newtype.

newtype N a = N a

instance Num a => Nullable (N a) where
    default_val = N 0

GHC also has an option to disable some of these rules. See http://www.haskell.org/ghc/docs/latest/html/users_guide/type-class-extensions.html

Also note that you can't put the ::a in there. It doesn't refer to the same a in the instance declaration, but is considered a universal a, which wouldn't make any sense there.

迷雾森÷林ヴ 2024-12-13 02:47:53

讨论

您的代码不是有效的 Haskell。不要误会我的意思 - 它是 Haskell 社区中常见的形式,并且可以由 GHC 编译,但我强烈建议您不要将“Haskell”与“Haskell 加上 GHC 可以提供的所有扩展”混淆。

1)您正在使用另一个类型类作为约束来实例化一个类型类,并且没有任何类型构造函数。在 Haskell 2010 中,您必须有一个类型构造函数:

相应实例声明的一般形式为:
实例cx′ => C (T u1 … uk) 其中 { d } 其中 k ≥ 0。 类型 (T u1
… uk) 必须采用应用于 simple 的类型构造函数 T 的形式
类型变量 u1, … uk;此外,T 不能是类型同义词,
并且用户界面必须全部不同。

GHC 在这里提供的扩展是 FlexibleInstances 和 UndecidableInstances

2) 您在实例中明确指定 default_val 类型。这是完全没有必要的,但如果您坚持的话,它也有一个名为 作用域类型变量

简短

您需要的扩展可以使用 语言杂注

{-# LANGUAGE FlexibleInstance UndecidableInstances ScopedTypeVariables #-}

或者在 ghcghci 的命令行(实际上只是一个包装纸ghc):

ghci -XFLexibleInstances -XUnderstandMyIntent

同样,许多扩展都是以 GHC 为中心的。你的代码永远不会在任何其他著名的 Haskell 编译器上运行,或者我的名字不是 John Meacham。

Discussion

Your code isn't valid Haskell. Don't get me wrong - it's in a form commonly seen in the Haskell community and it can be compiled by GHC, but I urge you not to confuse "Haskell" with "Haskell plus all the extensions GHC can offer".

1) You are instantiating a type class using another typeclass as a constraint and no type constructor what-so-ever. In Haskell 2010 you must have a type constructor:

The general form of the corresponding instance declaration is:
instance cx′ => C (T u1 … uk) where { d } where k ≥ 0. The type (T u1
… uk) must take the form of a type constructor T applied to simple
type variables u1, … uk; furthermore, T must not be a type synonym,
and the ui must all be distinct.

The extensions GHC offers here are FlexibleInstances and UndecidableInstances.

2) you explicitly give default_val a type in your instance. This is entirely unnecessary, but if you insist then this too has an extension called ScopedTypeVariables.

The Short

The extensions you need can be enabled either on a file-by-file basis using language pragma:

{-# LANGUAGE FlexibleInstance UndecidableInstances ScopedTypeVariables #-}

Or at the command line of either ghc or ghci (which actually is just a wrapper around ghc):

ghci -XFLexibleInstances -XUnderstandMyIntent

Again, many extensions are GHC-centric. Your code will never run on any other Haskell compiler of note or my name isn't John Meacham.

ゝ杯具 2024-12-13 02:47:53

还有另一种解决方案:此处描述的约束族:

http://dorchard.wordpress.com/2011/09/22/constraint-kinds-in-haskell-finally-bringing-us-constraint-families/

class Expr sem where
    constraint Pre sem a
    constant :: Pre sem a => a -> sem a
    add :: Pre sem a => sem a -> sem a -> sem a

instance Expr E where
    constraint Pre E a = Num a
    ...

它说它在 GHC 中可用HEAD 或 GHC 7.4(发布后)。

There's also another solution: constraint families described here:

http://dorchard.wordpress.com/2011/09/22/constraint-kinds-in-haskell-finally-bringing-us-constraint-families/

class Expr sem where
    constraint Pre sem a
    constant :: Pre sem a => a -> sem a
    add :: Pre sem a => sem a -> sem a -> sem a

instance Expr E where
    constraint Pre E a = Num a
    ...

It says it's available in GHC HEAD or GHC 7.4 (when released).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文