One of the minor annoyances of prefix notation for function call is that it gets the order of operations backwards. When you compose several functions into an expression, you generally have to write the last step first:
(handle-message (decrypt (receive socket) key))
If you explained this to a computer the same way you explain it to a human, you'd probably write the steps in the order they're performed. If you're used to Unix pipelines, you might write it like this:
receive socket | decrypt _ key | handle-message
In a language with user-defined infix operators, it's easy to support exactly this. You can define a |
operator which simply applies its right argument to its left — the same as Haskell's $
, but with the arguments reversed. It looks and feels very like the Unix |
: it connects the output of one expression to the input of the next. The channels used are return value and arguments rather than stdout and stdin, but the effect is the same.
I can't remember where (edit 16 Feb 2011: here, at least, and also |>
in F#), but I think I've heard this operator suggested before for Haskell — presumably with a different name, since |
is taken. Ignoring that inconvenient detail, its Haskell definition is simple:
infixl 0 | (|) :: a → (a → b) → b x | f = f x
-- Or, to make the similarity to$
clearer:(|) = flip ($)
Like $
, this is a contemptibly trivial operator. All it does is apply one argument to the other, which doesn't sound like it could possibly be worth spending an infix operator on. But I find myself using it constantly in pseudocode, because it lets me write operations in the right order. It doesn't make code shorter, but it significantly reduces the distance between the code I write and the representation in my head. That's important enough to be worth a one-character infix operator.
Like any high-order operator, |
is much more useful when you have a terse way to write simple functions. Usually this means partial application, either in the form of currying, or an explicit partial application operator, or op
, or implicit op
(as in the example above). Combinators are nice by themselves, but they need partial application to be really useful.
Or you simply use a concatenative language - like Factor (http://www.factorcode.org).
ReplyDeleteFunctional (by this I mean prefer to return new values over side-effects) C family OO code can actually look like this.
ReplyDeleteFor example
someObject.asCollection().reverse().map(foo).reduce(bar)
Looking at this code is painful to me (as my first experience with any language with a syntax like this was C++, which has scarred me for life), but it does have the properties that you suggest.
Yeah, this is one of two things I find surprisingly convenient about standard OO languages. The other is class scope, which greatly reduces name collisions. Order is superficial enough that it can be handled in other ways (e.g. the pipe operator), but naming is hard.
ReplyDeleteWith a dab of Unicode, we can solve this problem nicely in Haskell:
ReplyDeletetype Socket = ()
infixr 7 |>
(|>) = flip (.)
infixl 8 □
(□) = flip
receive :: Socket -> String
receive _ = "a message"
decrypt :: String -> String -> String
decrypt s _ = reverse s
handleMessage :: String -> String
handleMessage s = "Handling: " ++ s
socket :: Socket
socket = ()
key :: String
key = ""
example :: Socket -> String
example = receive |> decrypt □ key |> handleMessage
If some of the functions are monadic, all you need is a different operator -- in fact, Haskell already has such an operator (>>=).
Unicode is tempting, especially for ∘→⇒ and ∈⊆∩∪ and ≤≥≠∧∨ and of course λ. It's a bit of an input problem, though. Maybe an editor could automatically convert ASCII equivalents to Unicode, e.g. -> to →. This could be easily done in an Emacs mode; I wonder if it has?
ReplyDeleteThat □ operator only handles two-argument partial application, but that's still a substantial fraction. I'm not sure if it's more readable than prefix flip.
Nuh uh, it handles (f x □ y □ z) too. Two partial arguments next to each other is a bit problematic, but you can do:
ReplyDelete(f x □) □ y
which, admittedly, might be pushing the boundaries of readability.
Yes, that has been done in an Emacs mode before; Agda, for instance, is primarily written with a modified version of the TeX input method, such that \to turns into a right-arrow, as well as \forall and the like.
(f x □ y □ z) doesn't do what it looks like, though — it's (op f x _ y z), not (op f x _ y _ z). (I take your point that □ handles more than just the 2-argument case, but I don't think it's as easy as λ or op.)
ReplyDeletepostfix is nice because it transforms the input on the left into the output on the right.
ReplyDelete