If we made the types abstract and only exported smart constructors, we'd gain the benefit of being able to change the underlying representation without breaking client code. However, it would make the library a bit less nice to use, since pattern matching wouldn't be available.
What I usually do in my ML code is write a "view" type which lets me inject or project one layer of structure in a pattern-matchable way. Here's an example:
data Tree a -- abstract
data View f a = Tree a [f a]
inject :: View Tree a -> Tree a
project :: Tree a -> View Tree a
Then, users can use the inject,project gadgets to build and deconstruct trees, with pattern matching:
cons :: a -> [Tree a] -> Tree a
cons x ts = project $ Tree x ts
head :: Tree a -> a
head t =
case project t of
Tree x xs -> x
No need to implement this right away; but eventually, I think this is a pretty nice/painless approach that balances the two issues (abstraction vs. ease of use).
If we made the types abstract and only exported smart constructors, we'd gain the benefit of being able to change the underlying representation without breaking client code. However, it would make the library a bit less nice to use, since pattern matching wouldn't be available.
What I usually do in my ML code is write a "view" type which lets me inject or project one layer of structure in a pattern-matchable way. Here's an example:
Then, users can use the
inject,project
gadgets to build and deconstruct trees, with pattern matching:No need to implement this right away; but eventually, I think this is a pretty nice/painless approach that balances the two issues (abstraction vs. ease of use).