In Haskell Data.Set
implements a concrete set type. Normal []
lists also implement all operations of a set (in Data.List
). But there seems to be no predefined Set
typeclass that they both implement. You can implement one yourself:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}
module Set where
import qualified Data.List as ConcreteList
import qualified Data.Set as ConcreteSet
class Set set a | set -> a where
empty :: set
singleton :: a -> set
insert :: a -> set -> set
delete :: a -> set -> set
union :: set -> set -> set
intersection :: set -> set -> set
member :: a -> set -> Bool
filter :: (a -> Bool) -> set -> set
instance (Ord a) => Set (ConcreteSet.Set a) a where
empty = ConcreteSet.empty
singleton = ConcreteSet.singleton
insert = ConcreteSet.insert
delete = ConcreteSet.delete
union = ConcreteSet.union
intersection = ConcreteSet.intersection
member = ConcreteSet.member
filter = ConcreteSet.filter
instance (Eq a) => Set [a] a where
empty = []
singleton e = [e]
insert = (:)
delete = ConcreteList.delete
union = ConcreteList.union
intersection = ConcreteList.intersect
member = ConcreteList.elem
filter = ConcreteList.filter
but it seems that this would already have been done if this was the way to go. So my question is: Where is the Set
typeclass or what alternative solution has the Haskell community come up with?
My question is admittedly quite similar to Why is Haskell missing “obvious” Typeclasses, but by being more concrete (focusing on a specific example with sample implementation), I'm hoping to get some more concrete answers as well.
For whatever reason, Haskell tends not to do the whole thing of having deep hierarchies of type classes for representing all possible sorts of containers.
Generally, different sorts of containers have performance properties for different operations. Typically you know what operations are important to you, and you choose the most suitable container and use that explicitly. There's not much requirement to be able to write really generic code that operates over every possible kind of container.
Note that there are some type classes for dealing with containers already — they're just not the ones you might be expecting. For example:
Functor
allows you to apply a function to every element of a container — any container — and collect the results into a new container of the same type.
Applicative
and/or Monad
allow you to apply a function to every element and produce more than one element as result. (It may not be obvious, but you can use this to perform "filtering" and even "deletion", although it's probably not efficient.)
Monoid
provides your empty
and union
methods (but named mempty
and mappend
).
Alternative
, Foldable
and Traversable
let you do further interesting magic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With