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.
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.
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.
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.
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.
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
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 Int
s. 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 Int
s 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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With