使用类型类提升多态值
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
但是,使用此方法只能提升单态值。这
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
是否可以为多态值实现这个?
您应该始终将尽可能多的信息从实例头移动到实例约束。这里,正确的实例可以完全根据我们是否有 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
现在你所有的例子都有效了。
我尝试实现自动提升为 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
是否可以为多态值实现这个?
您应该始终将尽可能多的信息从实例头移动到实例约束。这里,正确的实例可以完全根据我们是否有 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
现在你所有的例子都有效了。