如何解决这种类型歧义?

Ben*_*Ben 2 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 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))
Run Code Online (Sandbox Code Playgroud)

无法编译时出现以下错误:

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> 
Run Code Online (Sandbox Code Playgroud)

我明白为什么会出现这种错误; 该调用merge可以使用Into与整个函数上的约束所选择的实例不同outer但具有不同但相同的实例.但我无法找到解决问题的方法.innerinto

我试过的事情:

  • 使用函数依赖来暗示outer来自inner; 这接近工作(抱怨需要UndecidableInstances),但似乎不太正确; 理想情况下,我真的希望能够将这种方法推向inner两个不同outer的方式
  • 使用关联类型同义词来做同样的事情; 除了uglifying类型签名(outer=> Outer inner)之外,我也因为outer我在一个实例中使用的类型变量(其中一个是幻像)而不是inner,这意味着我无法合法地实例化相关类型实例声明
  • 使用mergein intowith 添加显式类型签名ScopedTypeVariables以将其绑定到类型签名into; 但由于类型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
Run Code Online (Sandbox Code Playgroud)

kos*_*kus 5

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

解决方案是使类层次结构更精确.在这里,您可以创建第二个类merge:

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

class Mergeable inner => Into outer inner where
    factory :: inner -> outer
Run Code Online (Sandbox Code Playgroud)

您也许可以使用更通用的Semigroup类而不是ad-hoc Mergeable类,但这取决于应用程序的详细信息和属性merge.