Haskell 泛型:具有类的syb的当前状态

Haskell 泛型:具有类的syb的当前状态,haskell,Haskell,我正在尝试使用本文中描述的泛型编程。也就是说,能够“递归”用户定义类的成员,而不是编写遍历代码时已知的固定类型集 似乎相应的黑客软件包可以用于此目的,但大多数在线讨论(例如,7年前的这个问题:)都暗示当前是首选。特别是,这种实现似乎早于约束类型的使用,而约束类型的使用应该使这种编程更容易。然而,GHC.Generics框架似乎不允许使用可扩展函数进行遍历 现在用可扩展类型执行泛型函数的最佳替代方案是什么?如果可能的话,我希望避免使用“内部”表示(即任何类型的K1,M1等组合符),并且希望能够使用

我正在尝试使用本文中描述的泛型编程。也就是说,能够“递归”用户定义类的成员,而不是编写遍历代码时已知的固定类型集

似乎相应的黑客软件包可以用于此目的,但大多数在线讨论(例如,7年前的这个问题:)都暗示当前是首选。特别是,这种实现似乎早于约束类型的使用,而约束类型的使用应该使这种编程更容易。然而,
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)
因为generic
Size'
类确实是泛型的,所以可以将其推广到类似“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)
因为generic
Size'
类确实是泛型的,所以可以将其推广到类似“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)
。感谢您提供如此详细且非常有用的答案!这确实很好地解决了我正在处理的问题。感谢您提供如此详细和非常有用的答案!这确实很好地解决了我正在处理的问题。