Reputation: 23145
I've written the following function using generics-sop
. What it does, is given a value of a product type, goes through all it's members, applies a function to all those members, and spits out a list of the results:
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
Here's some example usage:
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
This will output the list [True, False, False]
.
So far so good. But with some code I'm trying to make generic now, the signature I need is slightly different. Instead of:
productToList :: ... => (forall b. c b => b -> t) -> a -> [t]
I want
productAccessorsToList :: ... => (forall b. c b => (a -> b) -> t) -> [t]
Basically instead of passing a value of a structure and iterating over the members, in this case don't have a value, but I want to iterate over the accessors functions of a type.
I've basically moved the a
from outside the forall
to inside the forall
.
What is an implementation of productAccessorsToList
?
Upvotes: 3
Views: 91
Reputation: 29193
The fundamental functionality is contained in projections
. Manipulate that.
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
You can recover productToList
from this, but I don't believe it works the other way around
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
Upvotes: 1