[Haskell-cafe] Some thoughts on Type-Directed Name Resolution
Gábor Lehel
illissius at gmail.com
Fri Feb 3 12:13:27 CET 2012
On Fri, Feb 3, 2012 at 10:30 AM, AntC <anthony_clayden at clear.net.nz> wrote:
> You seem to be not alone in wanting some special syntax for applying field
> selectors (see other posts on this thread). H98 field selectors don't do this,
> they're just functions.
>
> And there's me bending over backwards to make all Type-Directed overloaded-
> Name Resolution field selectors just functions, so you can mix field selectors
> and functions **without** special syntax. Example Yay!! above.
>
> I'm puzzled why you want different syntax for field selectors. Can you give
> some intuition?
Here's my problems with allowing postfix application using dot for all
functions.
The first problem is that mixing prefix and postfix function
application within the same line makes it harder to read. When you
read code to try to understand what it does, the direction you like to
go in is "here's some object, first do this to it, then do that to it,
then do this other thing to it, then this fourth thing to produce the
final result". In Haskell code with prefix application, this is easy:
you read it from right to left. In OO-style code using dots, it's even
easier: you read it from left to right. But if you mix the two, it's
much harder than either: you first have to figure out where the
sentence even begins, which is going to be somewhere in the middle,
and then every time the expression switches between prefix and
postfix, you have to figure out where to continue reading. The
algorithm your brain needs to follow is a lot branchier, so to speak.
This is the smaller problem. If prefix and postfix notations are
completely interchangeable, then we can at least expect people to not
make their own code hard to read, and to stick to one or the other
within an expression. (If they're *not* interchangeable, and one or
the other is required in some cases, then it's a bigger problem.)
The other problem is that, in order to make partial application
convenient, you want to put your function's parameters in the order of
least specific to most specific. If you want to make postfix
application convenient, you have to do the reverse.
For example, take the filter function from the Prelude:
filter :: (a -> Bool) -> [a] -> [a]
The order of its parameters makes it easy to write specialized filter
functions by partially applying filter, for example:
filterEvens = filter even
This is convenient and useful. (It's even more useful within
expressions, when you want to pass a function as an argument to a
higher-order function, which happens very frequently.) By contrast,
it's not usually useful to be able to specialize filter by the list it
filters, which is what you could conveniently do if the order of
filter's parameters were swapped:
filter :: [a] -> (a -> Bool) -> [a]
filterOneToTen = filter [1..10] -- ??
But for postfix function application, this latter order is the one you want:
[1..10].filter even
is a lot more intuitive than
even.filter [1..10]
So if you have postfix function application in the language, you end
up with a zero-sum situation where a function can be convenient to
partially apply, or it can be convenient to use with postfix notation,
but (unless it's single-argument) it can't be both. You'll end up with
some people preferring postfix notation and writing their functions
one way, other people preferring partial application and writing their
functions the other way, and a lot of frustration when people from one
group want to use functions written by the other. I hope you'll agree
that writing two versions of every function is not a satisfactory
solution. Having postfix application supply the last argument rather
than the first one -would- be satisfactory, but in Haskell's case it's
hard to tell which one that is. (Thanks to the fact that
multi-argument functions are just single-argument functions returning
other single-argument functions.)
Given this incompatibility, my humble opinion is that we should choose
one or the other. All of our existing functions, with only a few
irritating exceptions (writeIORef, I'm looking at you), are optimized
for partial application, so we should stick with it.
To finally get around to the point:
All of this said, record.field is still the most readable, intuitive,
and familiar syntax for selecting a field from a record that I know
of. It would be nice to have it. If we restrict this postfix notation
to only selecting fields from records, then the second problem from
above is completely obviated, and the first one is at least greatly
alleviated, to the point where I think the benefit outweighs the harm.
So my preferred solution is:
- Selecting fields from records can be written (equivalently) using
either prefix or postfix notation;
- Everything else can be written only with prefix notation.
My second-choice solution is to not introduce postfix notation.
More information about the Haskell-Cafe
mailing list