我使用generics-sop
编写了以下函数。它所做的是,给定一个product类型的值,遍历它的所有成员,对所有这些成员应用一个函数,然后输出结果列表:
import Generics.SOP qualified as SOP
import Generics.SOP hiding (Generic)
productToList :: forall c a t xs. (
IsProductType a xs,
SOP.Generic a,
ProductCode a ~ xs,
All c xs
)
=> (forall b. c b => b -> t) -> a -> [t]
productToList f = hcollapse . hcmap (Proxy :: Proxy c) (mapIK f) . productTypeFrom
下面是一些用法示例:
import Generics.SOP qualified as SOP
import Generics.SOP hiding (Generic)
data MyNums = MyNums { anInt :: Int, anInteger :: Integer, aFloat :: Float }
deriving stock Generic
deriving anyclass SOP.Generic
class (Eq a, Num a) => EqNum a
instance (Eq a, Num a) => EqNum a
c = MyNums { anInt = 0, anInteger = 5, aFloat = 7.2 }
f :: (Eq a, Num a) => a -> Bool
f x = x == 0
y :: [Bool]
y = genericProductToList @EqNum f c
这将输出列表[True, False, False]
。
到目前为止一切顺利。但是现在我正在尝试使一些代码泛型,我需要的签名略有不同。而不是:
productToList :: ... => (forall b. c b => b -> t) -> a -> [t]
我想要
productAccessorsToList :: ... => (forall b. c b => (a -> b) -> t) -> [t]
基本上不是传递结构体的值并遍历成员,在本例中没有值,但我想遍历类型的访问器函数。
我基本上把a
从forall
外面移到了forall
里面。
productAccessorsToList
的实现是什么?
基本功能包含在projections
中。操纵。
projectionsToList ::
forall c a t xs.
( IsProductType a xs
, SOP.Generic a
, ProductCode a ~ xs
, All c xs
)
=> (forall b. c b => (a -> b) -> t)
-> [t]
projectionsToList f =
hcollapse @_ @_ @NP @xs
$ hcmap (Proxy @c) adjust (projections @_ @I)
where
adjust :: forall x. c x => (K (NP I xs) -.-> I) x -> K t x
adjust p = K $ f $ unI . apFn p . K . productTypeFrom
你可以恢复productToList
从这个,但我不相信它工作的其他方式
productToList' ::
forall c a t xs.
( IsProductType a xs
, SOP.Generic a
, ProductCode a ~ xs
, All c xs
)
=> (forall b. c b => b -> t)
-> a -> [t]
productToList' f x = projectionsToList @c @a (f . ($ x))
-- would probably be slower than productToList