当前位置: 首页 > 知识库问答 >
问题:

为什么使用具有重叠实例的typeclass的函数在GHCI中表现不同?

鲁俊友
2023-03-14
{-# 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)

通常,我希望通过传递id作为其f参数(如上)来调用fpack,因此我希望将以下函数定义为速记:

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)
<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

共有1个答案

华君浩
2023-03-14

您需要手动实例化fpack

pack :: forall a r . FPack (a -> a) r => r
pack = fpack @(a->a) @r id

这需要ScopedTypeVariables,TypeApplications,AllowAmbiguousTypes

或者,提供id的类型。

pack :: forall a r . FPack (a -> a) r => r
pack = fpack (id :: a -> a)
 类似资料:
  • 为什么我不能在Java中做到这一点: 不会是子类。add已经实现Context。add,因为它add(Object)可以执行add(Integer)可以执行的所有操作? 解决这个问题的好方法是什么? 现在我正在做一种丑陋的方式: 编辑:这不是上述问题的副本。在给定的问题中,超类是以更一般的“Object”作为参数的类,而子类则更具体。这不起作用,因为更具体的方法可能无法处理任何对象。在我的问题中,

  • 问题内容: 在中,我们具有接口List和类: 并扩展AbstractList和 我的问题:为什么ArrayList有该implements List条款? 如果,我们不能说吗? 问题答案: 是。可以省略。但是,这是一个立即可见List。否则,将需要额外单击代码/文档。我认为这就是原因-清晰。 并补充的评论-这是为了展示该工具。总体而言,这只是为了方便起见,并减少了List实现之间的代码重复。

  • 我认为它使用我的头作为一个背景,实际上并没有计算它作为头,因为某种原因,这是我的代码: 它与这三条线重叠:

  • 谢谢你的帮助。Haskell看起来很有趣,但如果没有一个好教授的指导和指导,这是一门很难学的语言。我只是想自学这门语言。

  • 问题内容: 这个问题已经在这里有了答案 : 为什么我不应该在PHP中使用mysql_ *函数? (15个答案) 6年前关闭。 不久前我停止使用这些功能时,在这里扮演了Devil’s Advocate的角色,但问题是真实的,可能对许多SO用户来说很重要。 我们都知道以错误的方式使用功能可能非常危险,可能使您的网站容易受到攻击等。但是正确使用这些功能可以防止SQL注入,并且实际上比新的PDO功能要快一

  • 为了澄清我的问题,让我用一种或多或少相同的方式重新措辞: 为什么Haskell中有超类/类继承的概念?导致这种设计选择的历史原因是什么?例如,为什么有一个没有类层次结构的基库,只有相互独立的类型库是如此糟糕? 此外,我在类继承中看到的一个可能不太好的事情是:我认为一个类实例会默默地选择一个对应的超类实例,这可能是为该类型实现的最自然的实例。让我们把单子看作函子的子类。也许可以有不止一种方法来定义某