【问题标题】:Defaulting typeclass instances in Haskell and PureScriptHaskell 和 PureScript 中的默认类型类实例
【发布时间】:2026-01-18 05:45:01
【问题描述】:

我有一个简单的类,它在 Haskell/PureScript 中有一个 Int[]/Array 的实例:

HS:

class C c where f :: c -> c
instance C Int where f x = x
instance C c => C [c] where f l = map f l

PS:

class C c where f :: c -> c
instance intC :: C Int where f x = x
instance arrC :: C c => C (Array c) where f l = map f l

在这两种表示中,f [] 的值都是[]——不管c 类型的实例化。但是,当我写

f []

我得到一个模棱两可的错误:

HS:

    • Ambiguous type variable ‘a0’ arising from a use of ‘f’
      prevents the constraint ‘(C a0)’ from being solved.

PS:

  The inferred type
    forall t4. C t4 => Boolean
  has type variables which are not determined by those mentioned in the body of the type:
    t4 could not be determined

可以理解;在类字典分配期间确实存在混淆编译器的歧义。但是,在这种特殊情况下,这应该不是问题,因为唯一无法确定 c 的情况是列表为空时,因此结果已知时。

是否可以说服编译器将未设置的类型变量默认为特定的东西?例如,我想通过使用Int 实例化它们来自动解决该类的歧义。我知道 Num 类已经发生了这种情况:我什至有时会收到警告说“2137 在 Haskell 中默认为 Integer”。在询问两种语言时,我知道答案可能略有不同。

【问题讨论】:

  • 我很难想象有必要这样做的实际情况。你能扩展你的例子来说明你为什么需要这个吗?
  • 这个案子很具体,我不认为它会澄清问题的问题。我正在编写一个 Erlang-to-Purescript 转译器 (github.com/erlscripten),我在其中通过一些 ADT 表示 Erlang 术语。我想有一种方便的方法将任意 PS 表达式转换成那个,所以我有一个 ToErlang 类,它带有 toErlang :: a -> ErlangTerm 方法。有一个 ToErlangs 数组的实例,但是由于上面的问题,我需要指定空数组的类型,这很烦人。我很好奇它是否可以以某种(不)理智的方式完成
  • 我只要emptyArray :: ErlangTerm

标签: haskell typeclass purescript


【解决方案1】:

这是一种选择。

data CList a where
  CNil :: CList a
  CCons :: C a => a -> CList a -> CList a

instance C (CList a) where
  f CNil = CNil
  f (CCons x xs) = CCons (f x) (f xs)

对于一个简单的列表,我能想到的最好的方法是启用ExtendedDefaultRules 并定义

instance C () where
  f ~() = ()

我相信这应该将您的 [] 默认为 [] :: [()] 并做正确的事情。

【讨论】: