使用类型类提升多态值

Lifting polymorphic values with typeclasses

我尝试实现自动提升为 Either 类型:纯值将是 用 Right 提升,Either 个值用 id.

-- Wrapper for pure, non-Either values
newtype ConstRight a = ConstRight a

class LiftEither t e a where
  liftEither :: t -> Either e a

instance LiftEither (Either e a) e a where
  liftEither = id

instance LiftEither (ConstRight a) e a where
  liftEither (ConstRight a) = Right a

x :: Either () (Int -> Int)
x = liftEither $ ConstRight (id :: Int -> Int)

y :: Either () (a -> a)
y = liftEither $ ConstRight id

z :: Either (a -> a) ()
z = liftEither $ Left id

但是,使用此方法只能提升单态值。这 yz 的定义产生类型错误:

No instance for (LiftEither (ConstRight (a1 -> a1)) () (a -> a))
  arising from a use of ‘liftEither’
The type variable ‘a1’ is ambiguous

No instance for (LiftEither (Either (a0 -> a0) b0) (a -> a) ())
  arising from a use of ‘liftEither’
The type variables ‘b0’, ‘a0’ are ambiguous

这可以通过函数依赖来解决,但是ConstRight a不能确定e:

class LiftEither t e a | t -> e a where
  liftEither :: t -> Either e a

我也尝试过使用关联类型,但我找不到合适的 ConstRight 实例的定义:

class LiftEither t where
  type Result t
  liftEither :: t -> Result t

instance LiftEither (Either e a) where
  type Result (Either e a) = Either e a
  liftEither = id

instance LiftEither (ConstRight a) where
  type Result (ConstRight a) = Either e a -- e is not in scope
  liftEither (ConstRight a) = Right a

是否可以为多态值实现这个?

您应该始终将尽可能多的信息从实例头移动到实例约束。这里,正确的实例可以完全根据我们是否有 EitherConstRight 来确定,因此不需要进一步限制实例头。

LiftEither (Either e a) e a 过于严格,因为它要求两个 a-s 和两个 e-s 在匹配实例时可以确定为相等头。另一个实例有同样的问题。相反,您应该将类​​型等式移动到约束中。这样 GHC 可以愉快地匹配实例并稍后尝试解决约束。

{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, FlexibleInstances #-}

newtype ConstRight a = ConstRight a

class LiftEither t e a where
  liftEither :: t -> Either e a

instance (e ~ f, a ~ b) => LiftEither (Either e a) f b where
  liftEither = id

instance (a ~ b) => LiftEither (ConstRight a) e b where
  liftEither (ConstRight a) = Right a

现在你所有的例子都有效了。