[Haskell-cafe] The difficulty of designing a sequence class
Brian Hulley
brianh at metamilk.com
Mon Jul 31 20:43:23 EDT 2006
ajb at spamcop.net wrote:
> G'day all.
>
> Quoting Brian Hulley <brianh at metamilk.com>:
>
>> The problem is that some people will be using Data.Edison.Seq at the
>> moment and will naturally not want it to change. However I'd suggest
>> that all the common operations be factored out into separate classes
>> eg:
>
> While I think the huge typeclass is unfortunate, one of Edison's
> greatest strengths is that every sequence supports every sequence
> operation. (The catch, of course, is that the operation may be
> inefficient.)
>
> This was a deliberate design decision, and I'd be sorry to see it go.
> Many is the time in C++ when I started, say, with a std::stack, then
> discovered soon after that I needed to peer at the top few elements
> on the stack, only to find that std::stack doesn't support that.
As an aside, if I was needing any kind of sequence in C++ I'd use a
std::vector because it supplies all the ops you need (and is usually fast
enough for exploratory programming). I've never seen any point in stack or
deque etc because they're far too limited.
>
> Supporting all operations supports exploratory/agile programming. You
> don't have to decide up front what operations you need to be fast.
> You can discover this as you go.
>
> Yes, this is orthogonal to breaking up the huge typeclass, but I
> thought I'd just mention it.
As you've pointed out, there are 2 separate issues that are in danger of
being confused:
1) Forcing all sequence instances to support all operations
2) Bundling all the ops into a single huge class
I'd suggest that while 1) may be useful for the classes that are there at
present, there are many ops that they don't yet support and also some ops
that are never needed. Also, surely as long as there is one concrete type
that supports everything that should be good enough for exploratory
programming (I'm thinking of FingerTrees which seem to be able to do
absolutely anything in logarithmic time!!! :-) )
For 2), you could still have a Sequence class to gather all the separate
functionality together but I'd make it inherit from all the separate pieces
of functionality rather than being the place where all the functionality is
defined eg:
class Viewable c a | c -> a where
viewL :: Monad m => c -> m (a, c)
viewR :: Monad m => c -> m (c, a)
atL :: c -> a -- must be called on non-empty sequence
atR :: c -> a
class Indexable c a | c -> a where
length :: c -> Int
at :: Int -> c -> a -- index must be in range
splitAt :: Int -> c -> (c, c)
-- in same module as Indexable
take :: Indexable c a => Int -> c -> c
take i c = let (l, _) = splitAt i c in l
class (Monoid c, Foldable c a, Indexable c a, Filterable c a, Viewable
c a) => Sequence c a
This way, we'd get the advantages of being able to write (Sequence c a) as
well as the advantages of being able to supply a sequence to a function that
needed a Foldable - at the moment the fold methods of sequence are invisible
to the rest of Haskell because they're trapped inside the Sequence class.
Regards, Brian.
--
Logic empowers us and Love gives us purpose.
Yet still phantoms restless for eras long past,
congealed in the present in unthought forms,
strive mightily unseen to destroy us.
http://www.metamilk.com
More information about the Haskell-Cafe
mailing list