背景
我在 Haskell (GHC 8.6.3) 中编写了以下代码:
{-# LANGUAGE
NoImplicitPrelude,
MultiParamTypeClasses,
FlexibleInstances, FlexibleContexts,
TypeFamilies, UndecidableInstances,
AllowAmbiguousTypes
#-}
import Prelude(Char, Show, show, undefined, id)
data Nil
nil :: Nil
nil = undefined
instance Show Nil where
show _ = "nil"
data Cons x xs = Cons x xs
deriving Show
class FPack f r where
fpack :: f -> r
instance {-# OVERLAPPABLE #-} f ~ (Nil -> r) => FPack f r where
fpack f = f nil
instance (FPack (x -> b) r, f ~ (Cons a x -> b)) => FPack f (a -> r) where
fpack f a = fpack (\x -> f (Cons a x))
这段代码背后的想法是产生一个可变参数的函数,它接受它的参数并将它们打包到一个异构列表中。
例如,以下
fpack id "a" "b" :: Cons [Char] (Cons [Char] Nil)
产生列表Cons "a" (Cons "b" nil)
。
问题
一般来说,我想fpack
通过id
作为f
参数传递来调用(如上),所以我希望将以下函数定义为简写:
pack = fpack id
如果我将上面的程序加载到 GHCi 并执行上面的行,pack 会根据需要定义,并且它的类型(由 给出:t
)是FPack (a -> a) r => r
. 所以我在我的程序中定义了这样的函数:
pack :: FPack (a -> a) r => r
pack = fpack id
但这在将所述程序加载到 GHCi 时会出现以下错误:
bugs\so-pack.hs:31:8: error:
* Overlapping instances for FPack (a0 -> a0) r
arising from a use of `fpack'
Matching givens (or their superclasses):
FPack (a -> a) r
bound by the type signature for:
pack :: forall a r. FPack (a -> a) r => r
at bugs\so-pack.hs:30:1-29
Matching instances:
instance [overlappable] (f ~ (Nil -> r)) => FPack f r
-- Defined at bugs\so-pack.hs:24:31
instance (FPack (x -> b) r, f ~ (Cons a x -> b)) =>
FPack f (a -> r)
-- Defined at bugs\so-pack.hs:27:10
(The choice depends on the instantiation of `a0, r')
* In the expression: fpack id
In an equation for `pack': pack = fpack id
|
31 | pack = fpack id
|
这引出了我的问题。为什么这个函数在 GHCi 中定义时有效,但在程序中定义时无效?有没有办法让我在程序中正常工作?如果是这样,怎么做?
我的想法
根据我对 GHC 和 Haskell 的了解,这个错误来自于pack
可以解决两个重叠实例中的任何一个的事实,这会困扰 GHC。但是,我认为该AllowAmbiguousTypes
选项应该通过将实例选择推迟到最终调用站点来解决该问题。不幸的是,这显然还不够。我很好奇为什么,但我更好奇为什么 GHCi 在它的 REPL 循环中接受这个定义,但是当它在程序中时它不接受它。
切线
我有另一个关于这个程序的问题,它与这个问题的主旨没有直接关系,但我认为在这里问它可能是明智的,而不是就同一个程序创建另一个问题。
如上面的示例所示,即
fpack id "a" "b" :: Cons [Char] (Cons [Char] Nil)
我必须提供一个明确的类型签名fpack
才能使其按需要工作。如果我不提供(即只调用fpack id "a" "b"
),GHCi 会产生以下错误:
<interactive>:120:1: error:
* Couldn't match type `Cons [Char] (Cons [Char] Nil)' with `()'
arising from a use of `it'
* In the first argument of `System.IO.print', namely `it'
In a stmt of an interactive GHCi command: System.IO.print it
有什么办法可以改变的定义fpack
让 GHC 推断出正确的类型签名?