Search code examples
haskellgeneric-programming

Is there a generic function that takes a data structure and returns all ints in it?


I think the type signature would look like f :: a -> [Int] input data would look like data NamedPoint = NamedPoint String Int Int

data Person = Name Int Int Int

and using it in the REPL would look like this:

>> let namedPoint = NamedPoint "hey" 1 2
>> let tommy = Person "Tommy" 1 2 3
>> f namedPoint
>> [1,2] 
>> f Tommy
>> [1,2,3]

I think this would be useful as an alternative to records for when you are too lazy to write getters for data with alot of parameters.


Solution

  • The Data class is capable of this. I've found the easiest way to work with it is with the template traversal from the lens package. This essentially lets you set or get anything with a Data instance. In ghci:

    > import Data.Data
    > import Control.Lens
    > import Data.Data.Lens
    
    > -- Data is a derivable class
    > :set -XDeriveDataTypeable
    > data NamedPoint = NamedPoint String Int Int deriving (Data, Show)
    > data Person = Name String Int Int Int deriving (Data, Show)
    > let namedPoint = NamedPoint "hey" 1 2
    > let tommy = Name "Tommy" 1 2 3
    
    > let ints = toListOf template :: Data a => a -> [Int]
    > ints namedPoint
    [1,2]
    > ints tommy
    [1,2,3]
    

    Because template is a traversal, you can also map over values (but you may need to specify the type):

    > namedPoint & template *~ (10 :: Int)
    NamedPoint "hey" 10 20
    > import Data.Char
    > tommy & template %~ toUpper
    Name "TOMMY" 1 2 3