In my project I have a few data types that look like this
data StructureA = StructureA [Int] Bool Int
data StructureB = StructureB [String] String
My goal is to map functions over arrays contained inside instances of such datatypes
inst = StructureA [1,1,1] True 0
fmap (+1) inst -- [2,2,2]
My initial solution solution looks like this
instance Functor StructureA where
fmap (StructureA arr _ _) = fmap arr
However, I get kind mismatch error.
Question is how I can declare a function that can be polymorphically applied over such data structures?
You can only declare a Functor
instance over a parameterized type (* -> *
to be precise): a type that still needs an extra (and exactly one) type parameter.
So first we will need to introduce a type parameter. Even if you never plan to use something else than Int
s, we can easily abstract that away with:
data Structure a = Structure [a] Bool Int
We can for example declare the StructureA
as a type synomym:
type StructureA = Structure Int
Now we can make it a Functor
instance, by writing:
instance Functor Structure where
fmap f (Structure as b c) = ...
Note that we here did not write (Structure a)
, but Structure
, since - like we already said - fmap
has the freedom to change the type over which the collection works: the f
function can have for example type Int -> Char
to convert a Structure Int
to a Structure Char
.
Now we still need to implement fmap
. fmap
has type fmap :: Functor f => (a -> b) -> f a -> f b
so that means it takes a function, and in this case a Structure a
, and constructs a Structure b
. Based on your question (and the design decisions we have made), the only part we can map is the first parameter, so we construct a new Structure
, where the second parameter is the result of an fmap f
, but then over the second parameter, so:
instance Functor Structure where
fmap f (Structure as b c) = Structure (fmap f as) b c