Highness
Highness

Reputation: 353

Haskell Map Function for Algebraic Data types

I have an algebraic data type Newb defined like below. Now I want to write a custom map function for it without using recursion.Further I have a foldNewb function which could also help.

 data Newb a = Leaf a | Node [Newb a]


foldNewb:: (a->b)->([b]->b)->Newb a -> b
foldNewb f _ (Leaf a) = f a
foldNewb f1 f2 (Node a) = f2 (map (foldNewb f1 f2) a)


Newbmap :: (a->b)-> Newb a -> Newb b

Newbmap f (Leaf a) = (Leaf (f a))
Newbmap f (Node a) = (Node (foldNewb f concat a))

above is my attempt at implementing this function. I can't get any further than this and I don't understand what I'm doing wrong here. Any help would be appreciated.

Upvotes: 2

Views: 202

Answers (2)

Michiel Borkent
Michiel Borkent

Reputation: 34840

Without using foldNewb:

data Newb a = Leaf a | Node [ Newb a]
  deriving (Show)

newbMap :: (a -> b) -> Newb a -> Newb b
newbMap f (Leaf a) = Leaf (f a)
-- since a :: [Newb a] in the below clause, we can map (newbMap f) over the elements 
newbMap f (Node a) = Node (map (newbMap f) a)

tree = Node [ Leaf 1, Node [Leaf 2, Leaf 3], Leaf 4]
mapped = newbMap (+1) tree -- Node [Leaf 2,Node [Leaf 3,Leaf 4],Leaf 5]

Upvotes: 1

Silvio Mayolo
Silvio Mayolo

Reputation: 70297

tl;dr Here's your function. But I recommend reading on to see how I came up with this, so you can work out the thought process.

newbmap :: (a->b)-> Newb a -> Newb b
newbmap f = foldNewb (Leaf . f) Node

You're pretty close, and you're using foldNewb correctly, but you're overthinking it.

First, you can't name a function Newbmap. Capital names are reserved for types. So we'll call it newbmap. Now, foldNewb already handles both of the cases Leaf and Node, so we shouldn't have to pattern match in newbmap at all. In fact, your first newbmap case does exactly what foldNewb would do anyway, so let's just consider the second case.

newbmap :: (a->b)-> Newb a -> Newb b
newbmap f (Node a) = (Node (foldNewb f concat a))

We want to fold over our data structure. In particular, we want our fold call to completely produce the new data structure. We shouldn't need to explicitly use Node at the end, since, again, foldNewb already does that work for us.

newbmap :: (a->b)-> Newb a -> Newb b
newbmap f a = foldNewb f concat a

Now in the first case, we need a function a -> Newb b (since the result is going to be of type Newb b). You've passed f :: a -> b, which is very close to what you want. We simply need to compose it with a function b -> Newb b, and Leaf will do exactly that.

newbmap :: (a->b)-> Newb a -> Newb b
newbmap f a = foldNewb (Leaf . f) concat a

For the second argument, you want [Newb b] -> Newb b, which again is very easily done by Node.

newbmap :: (a->b)-> Newb a -> Newb b
newbmap f a = foldNewb (Leaf . f) Node a

And (although it doesn't make a difference), we can pointfree the final argument.

newbmap :: (a->b)-> Newb a -> Newb b
newbmap f = foldNewb (Leaf . f) Node

So there's a working newbmap function. Now, as for how I came up with all of those types, if you're using GHC, there's a very helpful feature called type holes that you can use to identify what sort of types you need. So (and I did exactly this when debugging your function) if you write

newbmap :: (a->b)-> Newb a -> Newb b
newbmap f = foldNewb _1 _2

then you'll get very specific GHC messages telling you _1 :: a -> Newb b and _2 :: [Newb b] -> Newb b. Then your challenge is simply to find functions with those particular types. And this is where I came up with Leaf . f and Node.

Upvotes: 5

Related Questions