6

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.

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
user514156
  • 630
  • 1
  • 7
  • 14
  • 3
    If the input data is going to be ``NamedPoint`` why cannot the function be typed as ``f :: NamedPoint -> [Int]``? – zegkljan Jul 03 '15 at 19:10
  • In this case it could. But I was thinking this behavior could be useful for extracting generalized types. ill edit my question to be more specific. – user514156 Jul 03 '15 at 19:19
  • For this task, uniplate is the simplest choice. Your f is simply the universeBi function. – augustss Jul 03 '15 at 22:22

2 Answers2

10

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
cchalmers
  • 2,896
  • 1
  • 11
  • 11
9

This is not possible with a function of the type signature you described. Think about what the f :: a -> [Int] means: f is supposed to be a function that takes a value of any possible type and returns a list of Ints. How should such a function be defined? The only possible definition is that it ignores the argument and returns a constant value, something like

f :: a -> [Int]
f _ = [0]

If you know what your a is going to be, why not just use that type? Like this:

f :: NamedPoint -> [Int]
f (NamedPoint _ a b) = [a, b]

If you wanted some "general" function returning all Ints from a datatype, one option would be to define a typeclass

class IntContainer a where
    f :: a -> [Int]

and then define instances for the datatypes you are interested in

instance IntContainer NamedPoint where
    f (NamedPoint _ a b) = [a, b]
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
zegkljan
  • 8,051
  • 5
  • 34
  • 49
  • 1
    "this is not possible" is a bit of an overstatement — it's not trivial and not, strictly speaking, possible with the exact signature mentioned (`a :: [Int]`) but it's definitely possible to achieve what the OP asks. – Erik Kaplun Jul 03 '15 at 21:26
  • @ErikAllik Thanks for your notice, I didn't know. I edited the answer to be more accurate. – zegkljan Jul 03 '15 at 21:31