Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 486
user514156 Avatar asked Jul 03 '15 19:07

user514156


People also ask

What is a generic data structure?

The most common generic data structures are maps (a.k.a. dictionaries) and arrays (or lists). But other generic data structures (e.g., sets, trees, and queues) can be used as well. Principle #2 does not deal with the mutability or the immutability of the data. That is the theme of Principle #3.

What is a generic program how it can be used in functions?

Generic Programming enables the programmer to write a general algorithm which will work with all data types. It eliminates the need to create different algorithms if the data type is an integer, string or a character. The advantages of Generic Programming are. Code Reusability. Avoid Function Overloading.

Why generic arrays are not allowed?

If generic array creation were legal, then compiler generated casts would correct the program at compile time but it can fail at runtime, which violates the core fundamental system of generic types.

Why primitives are not allowed in generics?

So, anything that is used as generics has to be convertable to Object (in this example get(0) returns an Object ), and the primitive types aren't. So they can't be used in generics.


2 Answers

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
like image 157
cchalmers Avatar answered Nov 03 '22 14:11

cchalmers


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]
like image 35
zegkljan Avatar answered Nov 03 '22 13:11

zegkljan