I have a small question about defining functions over types declared
with "newtype". Consider the following:
newtype MyList a = MyList [a]
myMap1 :: (a -> b) -> MyList a -> MyList b
myMap1 f (MyList []) = MyList []
myMap1 f (MyList (x:xs)) =
case myMap1 f (MyList xs) of MyList ys -> MyList (f x:ys)
myMap2 :: (a -> b) -> MyList a -> MyList b
myMap2 f (MyList xs) = MyList (myMap2' f xs)
myMap2' f [] = []
myMap2' f (x:xs) = f x : myMap2' f xs
unMyList :: MyList a -> [a]
unMyList (MyList xs) = xs
myMap3 :: (a -> b) -> MyList a -> MyList b
myMap3 f (MyList []) = MyList []
myMap3 f (MyList (x:xs)) = MyList (f x : unMyList (myMap3 f (MyList xs)))
or even, with the recent proposal for an extension to guards,
myMap4 :: (a -> b) -> MyList a -> MyList b
myMap4 f (MyList []) = MyList []
myMap4 f (MyList (x:xs))
| MyList ys <- myMap4 f (MyList xs) = MyList (f x:ys)
Should any of these rather cumbersome ways of defining the standard
map over (MyList a) cause any more code than the standard definition
of map over lists?
Is there a more elegant way of defining map over (MyList a)? (I am
aware that I could have used "map" in place of "myMap2'" but assume
for the moment that the function I am writing over (MyList a) has not
been defined over [a].)
I find it easy to tag a list to convert it to the type (MyList a), but
un-tagging (MyList a) to convert it back to [a] I find rather
cumbersome.
Perhaps there is no elegant solution to this, but as long as I can
rely on the above code generating no more work than necessary, I am
content.
Which of the above ways do people prefer?
-=GEM=-
PS. If anyone is wondering why I should want to use newtype, it comes
in very handy when defining implementations of ADTs that just use for
example a simple list representation. Introducing an explicit tag via
a "data" construction would increase space and time usage. Using a
"type" construction would not allow me to define instances of a class
for the ADT.