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
?
The fundamental functionality is contained in
projections
. Manipulate that.You can recover
productToList
from this, but I don't believe it works the other way around