我尝试将自动提升实现为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
。
y
然而,使用这种方法只能提升单一值。该
z
和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
实例的定义:
{{1}}
是否可以为多态值实现这一点?
答案 0 :(得分:3)
您应该始终将尽可能多的信息从实例头移动到实例约束。在这里,可以纯粹基于我们是Either
还是ConstRight
来确定正确的实例,因此不需要进一步约束实例头。
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
现在你的所有例子都有效。