[Haskell-cafe] Use of abbreviations in Haskell

Derek Elkins derek.a.elkins at gmail.com
Fri Jan 2 15:41:06 EST 2009

On Fri, 2009-01-02 at 15:20 +0100, Felix Martini wrote:
> Hi all,
> There is currently a discussion on reddit/programming about Haskell.
> One complaint is that Haskell functions often use abbreviated names. I
> tend to agree with that. In my personal experience it generally takes
> more time to learn a third party Haskell library than libraries
> written in other languages. I am not sure why but it could be because
> of function names. It seems to me that Haskell's current record syntax
> enhances this. Take for example the new xml library,
> data Element = Element {
>   elName :: QName
>   elAttribs :: [Attr]
>   elContent :: [Content]
>   elLine :: Maybe Line
> }
> data Attr = Attr {
>   attrKey :: QName
>   attrVal :: String
> }
> data QName = QName {
>   qName :: String
>   qURI :: Maybe String
>   qPrefix :: Maybe String
> }
> Personally i would prefer it to be something like
> data Element = Element {
>   name :: QualifiedName
>   attributes :: [Attribute]
>   content :: [Content]
>   line :: Maybe Line
> }
> data Attribute = Attribute {
>   key :: QualifiedName
>   value :: String
> }
> data QualifiedName = QualifiedName {
>   name :: String
>   uri :: Maybe String
>   prefix :: Maybe String
> }
> but the global scope of the record field names doesn't allow that 

They are not global.  They are scoped to the module just like any other
function definition.  So if you were to put each record in it's own
module you can have Attribute.key today.  No one does this though
because that's rather heavy-weight.

I've suggested in #haskell a few times an orthogonal language feature
that will resolve this as well as providing other nice things and, being
orthogonal, it keeps namespacing to namespacing mechanisms (namely
modules.)  The feature is simply local modules.

You'd get what you want by simply writing,

    module Attribute where
        data Attribute = Attribute {
            key :: QualifiedName,
            value :: String

I haven't been able to find any semantic difficulties with this
addition.  There are some choices, namely what to import and export by
default and some scoping issues.  My choices would be to import
everything in scope at the module declaration, the containing module
tacitly imports everything the contained module exports, the same
notation Module.name is used so a local module would shadow a top-level
(hierarchical) module of the same name (though I don't expect that to be
a common case.)  With these conventions there would often be nominal
mutual recursion between local modules at the same level.  The
implementation could either check to see if there is any actual mutual
recursion and give an error, or, much more preferably, simply allow
mutually recursive local modules as this should be much easier to handle
than mutually recursive top-level modules.

Some other benefits of this would be a nice way to make abstract data
types, which are also underused due to the heaviness of the module
system.  You could write, for example,

    module Stack (Stack, empty, push, pop, isEmpty) where
        newtype Stack a = Stack [a]
        empty = Stack []
        push x (Stack xs) = Stack (x:xs)
        pop (Stack (x:xs)) = Just (x, Stack xs)
        pop (Stack []    ) = Nothing
        isEmpty (Stack xs) = null xs

It should be straightforward to implement this today as a pre-processor
at the cost of not allowing local modules with the same qualified name
as a top-level module and losing some encapsulation.  The pre-processor
would simply need to extract all the local module declarations and make
the appropriate hierarchical modules and add the appropriate import
statements.  The easiest and most restrictive way to deal with mutual
recursion in this case is simply have a local module only import it's
ancestor modules and any modules explicitly imported.  The benefit of
this approach is that it doesn't require any kind of analysis, it could
be done on an almost purely textual basis.

> and
> therefore all kinds of abbreviations are inserted in front of the
> record field names which are hard to remember. So a better record
> syntax would be welcome. Perhaps the constructor could be used to
> limit the scope of the record field name e.g. QualifiedName.prefix?
> Regards,
> Felix
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe

More information about the Haskell-Cafe mailing list