Haskell 泛型:具有类的syb的当前状态
我正在尝试使用本文中描述的泛型编程。也就是说,能够“递归”用户定义类的成员,而不是编写遍历代码时已知的固定类型集 似乎相应的黑客软件包可以用于此目的,但大多数在线讨论(例如,7年前的这个问题:)都暗示当前是首选。特别是,这种实现似乎早于约束类型的使用,而约束类型的使用应该使这种编程更容易。然而,Haskell 泛型:具有类的syb的当前状态,haskell,Haskell,我正在尝试使用本文中描述的泛型编程。也就是说,能够“递归”用户定义类的成员,而不是编写遍历代码时已知的固定类型集 似乎相应的黑客软件包可以用于此目的,但大多数在线讨论(例如,7年前的这个问题:)都暗示当前是首选。特别是,这种实现似乎早于约束类型的使用,而约束类型的使用应该使这种编程更容易。然而,GHC.Generics框架似乎不允许使用可扩展函数进行遍历 现在用可扩展类型执行泛型函数的最佳替代方案是什么?如果可能的话,我希望避免使用“内部”表示(即任何类型的K1,M1等组合符),并且希望能够使用
GHC.Generics
框架似乎不允许使用可扩展函数进行遍历
现在用可扩展类型执行泛型函数的最佳替代方案是什么?如果可能的话,我希望避免使用“内部”表示(即任何类型的
K1
,M1
等组合符),并且希望能够使用类似的接口。任何指向论文、博客帖子或一般建议的提示都将不胜感激。好吧,这里有一篇博客帖子给你
如果您想按照“用类废弃样板文件”一文中的描述进行泛型编程,那么推荐的方法是使用syb with class
包,尽管有堆栈溢出的答案,因为syb with class
包是主动维护的,工作正常
如果您想直接使用GHC.Generics
对可扩展类型进行泛型编程,那么——就像直接使用GHC.generic
一样——您不能真正避免使用K1
、M1
等表示法。不幸的是,文档使这种表示听起来像是一种内部实现细节,随时可能发生更改
GHC.Generics
的潜在优势在于它自然地基于类型类,因此您可以免费获得类型扩展性。例如,以SYB with class paper中的gsize
为例,您可以在GHC.Generics
中直接使用一对类实现它,一个用于处理泛型结构,另一个用于处理沿途的特定类型:
-- Handle the generic structure
class Size' f where
size' :: f p -> Int
instance (Size' f) => Size' (M1 i c f) where
size' (M1 x) = size' x
instance (Size' f, Size' g) => Size' (f :+: g) where
size' (L1 x) = size' x
size' (R1 x) = size' x
instance (Size' f, Size' g) => Size' (f :*: g) where
size' (f :*: g) = size' f + size' g
instance (Size' U1) where
size' U1 = 0 -- constructor already counted by Size class
instance (Size' V1) where
size' _ = undefined
instance (Size c) => Size' (K1 i c) where
size' (K1 x) = size x
-- Handle the types
class Size t where
size :: t -> Int
default size :: (Generic t, Size' (Rep t)) => t -> Int
size t = 1 + size' (from t)
一般来说,不需要扩展Size'
,因为通过构造,它是一个类型无关的通用实现,将拥有一组详尽(或几乎详尽)的实例。但是,Size
type类显然是开放的,可以随意扩展:
data Name = N String
instance Size Name where
size (N _) = 1
-- a fanciful example of a custom recursive type
newtype Negative a = Neg a
instance Size a => Size (Negative a) where
size (Neg x) = -size x
-- a user-defined type using a default instance
data Something = Something Int (Name, Name) Bool deriving (Generic)
instance Size Something
-- needs some supporting default instances:
instance Size Bool
instance (Size a, Size b) => Size (a,b)
-- and a custom instance. This could be defaulted, but
-- then we'd need an instance for unboxed Int#
instance Size Int where size _ = 1
main = do
print $ size (Something 10 (N "John", N "Doe") False)
print $ size (Neg (1 :: Int, 2 :: Int), True)
因为genericSize'
类确实是泛型的,所以可以将其推广到类似“SYB with class”的查询,我们可以使用ConstraintKinds
使语法更加清晰:
class Query' cls f where
gmapQm :: Monoid a => Proxy cls -> (forall t. cls t => t -> a) -> f p -> a
instance (Query' cls f) => Query' cls (M1 i c f) where
gmapQm p h (M1 x) = gmapQm p h x
instance (Query' cls f, Query' cls g) => Query' cls (f :+: g) where
gmapQm p h (L1 x) = gmapQm p h x
gmapQm p h (R1 x) = gmapQm p h x
instance (Query' cls U1) where
gmapQm _ _ U1 = mempty
instance (Query' cls f, Query' cls g) => Query' cls (f :*: g) where
gmapQm p h (f :*: g) = gmapQm p h f <> gmapQm p h g
instance (cls c) => Query' cls (K1 i c) where
gmapQm p h (K1 x) = h x
好吧,这里有一篇博文给你 如果您想按照“用类废弃样板文件”一文中的描述进行泛型编程,那么推荐的方法是使用
syb with class
包,尽管有堆栈溢出的答案,因为syb with class
包是主动维护的,工作正常
如果您想直接使用GHC.Generics
对可扩展类型进行泛型编程,那么——就像直接使用GHC.generic
一样——您不能真正避免使用K1
、M1
等表示法。不幸的是,文档使这种表示听起来像是一种内部实现细节,随时可能发生更改
GHC.Generics
的潜在优势在于它自然地基于类型类,因此您可以免费获得类型扩展性。例如,以SYB with class paper中的gsize
为例,您可以在GHC.Generics
中直接使用一对类实现它,一个用于处理泛型结构,另一个用于处理沿途的特定类型:
-- Handle the generic structure
class Size' f where
size' :: f p -> Int
instance (Size' f) => Size' (M1 i c f) where
size' (M1 x) = size' x
instance (Size' f, Size' g) => Size' (f :+: g) where
size' (L1 x) = size' x
size' (R1 x) = size' x
instance (Size' f, Size' g) => Size' (f :*: g) where
size' (f :*: g) = size' f + size' g
instance (Size' U1) where
size' U1 = 0 -- constructor already counted by Size class
instance (Size' V1) where
size' _ = undefined
instance (Size c) => Size' (K1 i c) where
size' (K1 x) = size x
-- Handle the types
class Size t where
size :: t -> Int
default size :: (Generic t, Size' (Rep t)) => t -> Int
size t = 1 + size' (from t)
一般来说,不需要扩展Size'
,因为通过构造,它是一个类型无关的通用实现,将拥有一组详尽(或几乎详尽)的实例。但是,Size
type类显然是开放的,可以随意扩展:
data Name = N String
instance Size Name where
size (N _) = 1
-- a fanciful example of a custom recursive type
newtype Negative a = Neg a
instance Size a => Size (Negative a) where
size (Neg x) = -size x
-- a user-defined type using a default instance
data Something = Something Int (Name, Name) Bool deriving (Generic)
instance Size Something
-- needs some supporting default instances:
instance Size Bool
instance (Size a, Size b) => Size (a,b)
-- and a custom instance. This could be defaulted, but
-- then we'd need an instance for unboxed Int#
instance Size Int where size _ = 1
main = do
print $ size (Something 10 (N "John", N "Doe") False)
print $ size (Neg (1 :: Int, 2 :: Int), True)
因为genericSize'
类确实是泛型的,所以可以将其推广到类似“SYB with class”的查询,我们可以使用ConstraintKinds
使语法更加清晰:
class Query' cls f where
gmapQm :: Monoid a => Proxy cls -> (forall t. cls t => t -> a) -> f p -> a
instance (Query' cls f) => Query' cls (M1 i c f) where
gmapQm p h (M1 x) = gmapQm p h x
instance (Query' cls f, Query' cls g) => Query' cls (f :+: g) where
gmapQm p h (L1 x) = gmapQm p h x
gmapQm p h (R1 x) = gmapQm p h x
instance (Query' cls U1) where
gmapQm _ _ U1 = mempty
instance (Query' cls f, Query' cls g) => Query' cls (f :*: g) where
gmapQm p h (f :*: g) = gmapQm p h f <> gmapQm p h g
instance (cls c) => Query' cls (K1 i c) where
gmapQm p h (K1 x) = h x
很多uniplate的东西现在都在
lens
中。我用over template(f::A->A)
代替无处不在(f::A->A)
。很多uniplate的东西现在都在lens
中。我用over template(f::A->A)
代替无处不在(f::A->A)
。感谢您提供如此详细且非常有用的答案!这确实很好地解决了我正在处理的问题。感谢您提供如此详细和非常有用的答案!这确实很好地解决了我正在处理的问题。