Babra Cunningham
Babra Cunningham

Reputation: 2967

Performing operations on custom data types?

I have the following data type:

data Users = Height Int | Age Int 

I then have a list of ages:

myList = [Age 44, Age 54, Age 21, Age 34, Age 22]

Which I want to apply to this function:

myFunction :: [Users] -> [Users]
myFunction li = [x + 1 | x <- li]

However this causes the following error:

"No instance for (Num Users) arising from a use of ‘+’"

How can I make this work? Do I need to separate 'Age' from each of the values?

Upvotes: 1

Views: 244

Answers (4)

Babra Cunningham
Babra Cunningham

Reputation: 2967

I've found the best solution using Applicative Functors.

Code:

data Users a = Height a | Age a deriving (Show, Eq, Ord)

myList = [Age 44, Age 65, Age 21, Age 87]

instance Functor Users where
   fmap f (Age a) = Age (f a)
   fmap f (Height a) = Height (f a)

instance Applicative Users where
   pure a = (Age a)
   (<*>) (Age a) = fmap a
   pure a = (Height a)
   (<*>) (Height a) = fmap a

main = do
   let increaseAgesByOne = pure (\x -> pure (+1) <*> x) <*> myList
   print $ increaseAgesByOne

Output:

[Age 45, Age 66, Age 22, Age 88]

Hope this helps anyone with a similar problem.

Upvotes: -1

ljedrz
ljedrz

Reputation: 22273

In your case I think you don't really want a new data type

data Users = Height Int | Age Int 

Type synonyms should suffice; they will work nicely with standard operators:

type Age = Int
type Height = Int

myList :: [Age]
myList = [44, 54, 21, 34, 22]

myFunction :: [Age] -> [Age]
myFunction li = [x + 1 | x <- li]

Upvotes: 2

Chuck Aguilar
Chuck Aguilar

Reputation: 2048

@Schoon is right.

But if you want to make it on your way you can do this:

data Users = Height Int | Age Int deriving (Show) you have to "derive" show ;)

then:

older :: Users -> Users
older (Age a) = Age (a+1)
older _       = error "not Age" --Now it's better ;)

and your function:

everyOneOlder :: [Users] -> [Users]
everyOneOlder li = [older x | x <- li]

And then, you become this:

*Main> :l test.hs 
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> let x = Age 5
*Main> x
Age 5
*Main> let y = older x
*Main> y
Age 6
*Main> let z = [Age 1, Age 2]    
*Main> everyOneOlder z
[Age 2,Age 3]
*Main> 

Nice, isn't it? :)

Upvotes: 1

Schoon
Schoon

Reputation: 394

First, the error message tells you that you are using the (+) function on Users, for which it is not defined.

So you could make Users an instance of Num, meaning that you also need to define (-), (*), negate,.. for Users, which seems odd.

Maybe you need something like this:

data User = User {height :: Int, age :: Int } deriving (Show)

addToHeight :: Int -> User -> User
addToHeight x (User h a) = User (h+x) a

then use:

let users = [User 180 20, User 185 22]
fmap (addToHeight 1) users

--

to leave semantics aside:

plus :: Int -> Users -> Users
plus x (Age a) = Age (a+x)
plus x (Height h) = Height (h+x)

Upvotes: 8

Related Questions