I'm probably doing this completely wrong (the unhaskell way); I'm just learning so please let me know if there's a better way to approach this.
Context: I'm writing a bunch of tree structures. I want to reuse my prettyprint function for binary trees. Not all trees can use the generic Node/Branch data type though; different trees need different extra data. So to reuse the prettyprint function I thought of creating a class different trees would be instances of:
class GenericBinaryTree a where
is_leaf :: a -> Bool
left :: a -> a
node :: a -> b
right :: a -> a
This way they only have to implement methods to retrieve the left, right, and current node value, and prettyprint doesn't need to know about the internal structure.
Then I get down to here:
prettyprint_helper :: GenericBinaryTree a => a -> [String]
prettyprint_helper tree
| is_leaf tree = []
| otherwise = ("{" ++ (show (node tree)) ++ "}") : (prettyprint_subtree (left tree) (right tree))
where
prettyprint_subtree left right =
((pad "+- " "| ") (prettyprint_helper right)) ++ ((pad "`- " " ") (prettyprint_helper left))
pad first rest = zipWith (++) (first : repeat rest)
And I get the Ambiguous type variable 'a0' in the constraint: (Show a0) arising from a use of 'show' error for (show (node tree))
Here's an example of the most basic tree data type and instance definition (my other trees have other fields but they're irrelevant to the generic prettyprint function)
data Tree a
= Branch (Tree a) a (Tree a)
| Leaf
instance GenericBinaryTree (Tree a) where
is_leaf Leaf = True
is_leaf _ = False
left (Branch left node right) = left
right (Branch left node right) = right
node (Branch left node right) = node
I could have defined node :: a -> [String] and deal with the stringification in each instance/type of tree, but this feels neater. In terms of prettyprint, I only need a string representation, but if I add other generic binary tree functions later I may want the actual values.
So how can I write this to work whether the node value is an instance of Show or not? Or what other way should I be approaching this problem? In an object oriented language I could easily check whether a class implements something, or if an object has a method.
I can't use something like
prettyprint :: Show a => a -> String
Because it's not the tree that needs to be showable, it's the value inside the tree (returned by function node) that needs to be showable. I also tried changing node to Show b => a -> b without luck (and a bunch of other type class/preconditions/whatever/I don't even know what I'm doing anymore).