logn
logn

Reputation: 150

instance Functor with data

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

Answers (3)

leftaroundabout
leftaroundabout

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

Random Dev
Random Dev

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 * again

it'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

Will Ness
Will Ness

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

Related Questions