使用 GADT 为类型类构造具体类型
Constructing concrete types for typeclasses with GADTs
这样做的灵感是创建一个值列表,这些值是 Show
的实例。我发现以下代码片段使用 GADT 创建具体的 Showable
类型。
data Showable where Showable :: Show a => a -> Showable
instance Show Showable where
show (Showable x) = show x
list :: [Showable]
list = [Showable 4, Showable "hello", Showable 'a']
然后,我试图通过创建一个可以使任何类型类具体化的类型来使 Showable
更通用。
data Concrete a where Concrete :: a b => b -> Concrete a
instance Show (Concrete Show) where
show (Concrete x) = show x
list :: [Concrete Show]
list = [Concrete 4, Concrete "hello", Concrete 'a']
这适用于 ConstraintKinds 和 FlexibleInstances 语言扩展,但为了使用 Concrete
为其他类型类创建具体类型,每个都需要一个新实例。
有没有办法创建类似于 Concrete
的东西,例如 Concrete Show
自动成为 Show
的实例?
这是不可能的。考虑一下:
instance Monoid (Concrete Monoid) where
mappend (Concrete x) (Concrete y) = Concrete (mappend x y) -- type error!
这是一个类型错误,因为 x
和 y
来自两个不同的存在量化。不保证x
和y
可以加在一起
换句话说,[Concrete [1,2], Concrete ["hello"]]
有类型[Concrete Monoid]
但不能求和(mconcat
)。
这正是同一个问题,在 OOP 中,以下基础 class/interface 不起作用:
interface Vector {
Vector scale(double x);
Vector add(Vector v);
}
class Vec2D implements Vector { ... }
class Vec3D implements Vector { ... }
界面暗示 2D 向量可以添加到任何其他向量,包括 3D 向量,这是没有意义的。有关 OOP 解决方案,请参阅 F-bounded quantification and its related popularization callled curiously recurring template pattern。
在 Haskell 中,我们通常不需要这样的技术,因为没有子类型,因此,Vector
类型 class 中的两种类型已经不可混合。
class Vector a where
scale :: Double -> a -> a
add :: a -> a -> a
instance Vector (Vec2D) where ...
instance Vector (Vec3D) where ...
这样做的灵感是创建一个值列表,这些值是 Show
的实例。我发现以下代码片段使用 GADT 创建具体的 Showable
类型。
data Showable where Showable :: Show a => a -> Showable
instance Show Showable where
show (Showable x) = show x
list :: [Showable]
list = [Showable 4, Showable "hello", Showable 'a']
然后,我试图通过创建一个可以使任何类型类具体化的类型来使 Showable
更通用。
data Concrete a where Concrete :: a b => b -> Concrete a
instance Show (Concrete Show) where
show (Concrete x) = show x
list :: [Concrete Show]
list = [Concrete 4, Concrete "hello", Concrete 'a']
这适用于 ConstraintKinds 和 FlexibleInstances 语言扩展,但为了使用 Concrete
为其他类型类创建具体类型,每个都需要一个新实例。
有没有办法创建类似于 Concrete
的东西,例如 Concrete Show
自动成为 Show
的实例?
这是不可能的。考虑一下:
instance Monoid (Concrete Monoid) where
mappend (Concrete x) (Concrete y) = Concrete (mappend x y) -- type error!
这是一个类型错误,因为 x
和 y
来自两个不同的存在量化。不保证x
和y
可以加在一起
换句话说,[Concrete [1,2], Concrete ["hello"]]
有类型[Concrete Monoid]
但不能求和(mconcat
)。
这正是同一个问题,在 OOP 中,以下基础 class/interface 不起作用:
interface Vector {
Vector scale(double x);
Vector add(Vector v);
}
class Vec2D implements Vector { ... }
class Vec3D implements Vector { ... }
界面暗示 2D 向量可以添加到任何其他向量,包括 3D 向量,这是没有意义的。有关 OOP 解决方案,请参阅 F-bounded quantification and its related popularization callled curiously recurring template pattern。
在 Haskell 中,我们通常不需要这样的技术,因为没有子类型,因此,Vector
类型 class 中的两种类型已经不可混合。
class Vector a where
scale :: Double -> a -> a
add :: a -> a -> a
instance Vector (Vec2D) where ...
instance Vector (Vec3D) where ...