Haskell 如何解决这种类型的类歧义?

Haskell 如何解决这种类型的类歧义?,haskell,typeclass,Haskell,Typeclass,下面是我的最简单的例子: {-# LANGUAGE MultiParamTypeClasses, RankNTypes #-} import Control.Lens class Into outer inner where factory :: inner -> outer merge :: inner -> inner -> inner -- Given an inner item, a lens and an outer item, use fact

下面是我的最简单的例子:

{-# LANGUAGE MultiParamTypeClasses, RankNTypes #-}

import Control.Lens

class Into outer inner where
    factory :: inner -> outer
    merge :: inner -> inner -> inner

-- Given an inner item, a lens and an outer item, use factory to construct a new
-- outer around the inner if the Maybe outer is Nothing, or else use merge to combine
-- the argument inner with the one viewed through the lens inside the outer
into :: Into outer inner =>
    inner -> Lens' outer inner -> Maybe outer -> Maybe outer
inner `into` lens = Just . maybe (factory inner) (over lens (merge inner))
编译失败,出现以下错误:

GHCi, version 7.6.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 1] Compiling Main             ( foo.hs, interpreted )

foo.hs:10:62:
    Could not deduce (Into outer0 inner) arising from a use of `merge'
    from the context (Into outer inner)
      bound by the type signature for
                 into :: Into outer inner =>
                         inner -> Lens' outer inner -> Maybe outer -> Maybe outer
      at foo.hs:9:9-84
    The type variable `outer0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    In the second argument of `over', namely `(merge inner)'
    In the second argument of `maybe', namely
      `(over lens (merge inner))'
    In the second argument of `(.)', namely
      `maybe (factory inner) (over lens (merge inner))'
Failed, modules loaded: none.
Prelude> 
我理解为什么会发生这种错误;对
merge
的调用可以使用不同的
Into
实例(使用不同的
外部
,但相同的
内部
),而不是整个
Into
函数的约束所选择的实例。但我想不出解决的办法

我尝试过的事情:

  • 使用函数依赖关系从
    内部
    暗示
    外部
    ;这几乎可以奏效(抱怨需要
    不可判定的实例
    ),但似乎不太正确;理想情况下,我真的希望能够有一种方法将相同的
    内部
    推入两个不同的
    外部
    s
  • 使用关联的类型同义词执行相同的操作;除了丑化类型签名(
    outer
    =>
    outer-inner
    ),我还因为实例中使用的
    outer
    inner
    有更多类型变量(其中一个是幻影),这意味着我无法在实例声明中合法实例化关联的类型
  • 使用
    ScopedTypeVariables
    中的
    merge
    添加到
    中的显式类型签名,以将其与
    的类型签名绑定到
    ;但是由于
    merge
    的类型不涉及
    outer
    ,因此没有帮助
有没有什么方法可以明确说明,对于
merge
和整个
into
使用相同的类型类实例?或者我可以通过任何其他方式约束类型系统,使其需要这样做?理想情况下,我希望保留该类,因此我的实例声明仍然很简单:

instance (Hashable v, Eq v) => Into (VarInfo s k v) (HashSet v) where
    -- VarInfo is just a record type with 2 fields, the second being a HashSet v
    factory = VarInfo (return ())
    merge = HashSet.intersection

拥有一个不提及所有类变量的类方法很少是一个好主意(除非这些类变量是由函数依赖项唯一确定的)

解决方案是使类层次结构更加精确。在这里,您可以为
合并
创建第二个类:

class Mergeable a where
    merge :: a -> a -> a

class Mergeable inner => Into outer inner where
    factory :: inner -> outer

您可能也可以使用更通用的
半群
类,而不是特殊的
可合并
类,但这取决于应用程序的详细信息和
合并
的属性。注意:
Lens'
是多态的,因此将其作为参数的函数将获得秩2类型。如果您只是在上使用
,您可以为它提供一个更简单的rank-1类型(由于它适用于
Setter等,因此它也将更多态)。(即使你正在使用每一个镜头操作,你也可以接受
ALens'
,然后使用
cloneLens
,或者类似的东西。)@shachaf谢谢;我仍然对镜头包里的所有东西抱着不放。我知道
Lens'
没有它所需要的那么普遍,但我有镜头,我知道
Lens'
的意思,而在这个阶段,该参数的推断类型对我来说是不可理解的胡言乱语(不确定它的友好同义词是什么,如果有的话)。秩-1类型的优点是什么?主要优点是秩-2类型的类型推断在GHC中不太有效。您最终需要扩展、编写GHC通常能够推断的显式类型,等等。该类型也会传播到您函数的每个用户——您需要的多态性比需要的更多。(可能还有性能方面的影响,因为除非事情是内联的,否则您需要传递显式字典……不过我还没有检查。)顺便说一下,如果您想了解
lens
类型等方面的说明,请随时访问Freenode上的
#haskell lens
!干杯,这似乎是显而易见的。我已经考虑过在合并操作中使用幺半群,但是我的一个例子是集合交集,交集下集合的幺半群的零必须是元素类型的完整集合。制作一个与幺半群相矛盾的半群实例不是一个好主意。在我的另一个例子中,我不认为这个操作是关联的。这个类只是内部的,没有暴露在我的界面中,所以我不太关心它是否有点专业化。