这是我的最小示例:
{-# 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
(具有不同outer
但相同的实例)。但我想不出解决它的方法。inner
into
我尝试过的事情:
- 使用函数依赖来暗示
outer
frominner
; 这接近工作(抱怨需要UndecidableInstances
),但似乎不太正确;理想情况下,我真的希望能够有一种方法将相同的东西推inner
入两个不同outer
的 s - 使用关联的类型同义词来做同样的事情;除了丑化类型签名(
outer
=>Outer inner
)之外,我还摔倒了,因为outer
我在实例中使用的类型变量(其中一个幻像)比inner
,这意味着我无法合法地实例化关联的类型实例声明 merge
在使用ininto
with时添加显式类型签名ScopedTypeVariables
以将其绑定到类型签名 forinto
; 但由于类型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