Reputation: 150
i am new to Haskell but i implemented this:
data Triple = T Double Double Double
instance Functor Triple where
fmap f (T a b c) = T (f a) (f b) (f c)
then i tried to make the Triple
a Functor
but
i get this message:
**error:
• Expected kind ‘* -> *’, but ‘Triple’ has kind ‘*’
• In the first argument of ‘Functor’, namely ‘Triple’
In the instance declaration for ‘Functor Triple**’
so my question is , do i really have to change the data Constructor to
data Triple a = a a a
???
this is my solution but is there a better one ?
Upvotes: 1
Views: 291
Reputation: 120711
this is my solution but is there a better one ?
Well, depends on what you mean by “better”. Triple a
makes a lot of sense, if it's not really specific for Double
.
If you have good reasons to hard-code Double
, then you can always just make a dedicated function for mapping over this data type, like tripleMap
in Carsten's solution. There is also a widely used typeclass for this sort of operation though:
{-# LANGUAGE TypeFamilies #-}
import Data.MonoTraversable
type instance Element Triple = Double
instance MonoFunctor Triple where
omap f (T a b c) = T (f a) (f b) (f c)
Consider also whether it even makes sense to map arbitrary functions over your type. Maybe what you actually want is this?
import Data.AdditiveGroup
import Data.VectorSpace
data ℝ³ = ℝ³ Double Double Double
instance AdditiveGroup ℝ³ where
zeroV = ℝ³ 0 0 0
ℝ³ x y z ^+^ ℝ³ ξ υ ζ = ℝ³ (x+ξ) (y+υ) (z+ζ)
negateV (ℝ³ x y z) = ℝ³ (-x) (-y) (-z)
instance VectorSpace ℝ³ where
type Scalar ℝ³ = Double
μ *^ ℝ³ x y z = ℝ³ (μ*x) (μ*y) (μ*z)
Upvotes: 2
Reputation: 52270
yes, you need a * -> *
Type for a Functor.
It has to be like this as the type of fmap
is fmap :: (a -> b) -> f a -> f b
- so as you can see f
is applied to the type-parameter a
in there.
So yep this would work:
data Triple a = T a a a
instance Functor Triple where
fmap f (T a b c) = T (f a) (f b) (f c)
* -> *
means you need a type-constructor that is waiting for a Type and returns a Type.
For example:
Double
has kind *
Triple
(here) has kind * -> *
Triple Double
has kind *
againit's like a curried function on the type-level ;)
you could write something like this for your Triple
though:
data Triple = T Double Double Double
tripleMap :: (Double -> Double) -> Triple -> Triple
tripleMap (T a b c) = T (f a) (f b) (f c)
and maybe this is fine enough for you.
Upvotes: 2
Reputation: 71070
The answer is yes, you need to define
data Triple a = T a a a
instance Functor Triple where
fmap f (T x y z) = T (f x) (f y) (f z)
I use x, y, z
to stress that these are not types, but values.
Triple a
is the type. Its values carry inside them three values of the same type, a
, whichever that is in each specific case, like Double
or whatever.
Since fmap :: (a -> b) -> f a -> f b
, in the case of Triple
it is fmap :: (a -> b) -> Triple a -> Triple b
, and values of Triple b
type carry inside them three values of the same type, b
.
Thus we had to transform each of the three inside Triple Double
, etc.
Upvotes: 1