Hacker News new | ask | show | jobs
by kibwen 437 days ago
The tragedy of Lisp is that postfix-esque method notation just plain looks better, especially for people with the expectation of reading left-to-right.

    let bar_x = x.bar()
    let quux_y = y.quux()
    return (bar_x, quux_y, z).foo()
3 comments

Looks better is subjective, but it has its advantages both for actual autocomplete - as soon as I hit the dot key my IDE can tell me the useful operations for the obejct - and also for "mental autocomplete" - I know exactly where to look to find useful operations on the particular object because they're organized "underneath" it in the conceptual hierarchy. In Lisps (or other languages/codebases that aren't structured in a non-OOP-ish way) this is often a pain point for me, especially when I'm first trying to make my way into some code/library.

As a bit of a digression:

The ML languages, as with most things, get this (mostly) right, in that by convention types are encapsulated in modules that know how to operate on them - although I can't help but think there ought to be more than convention enforcing that, at the language level.

There is the problem that it's unclear - if you can Frobnicate a Foo and a Baz together to make a Bar, is that an operation on Foos, on Bazes, or on Bars? Or maybe you want a separate Frobnicator to do it? (Pure) OOP languages force you to make an arbitrary choice, Lisp and co. just kind of shrug, the ML languages let you take your take your pick, for better or worse.

It's not really subjective because people have had the opportunity to program in the nested 'read from the inside out' style of lisp for 50 years and almost no one does it.
I think the cost of Lisp machines was the determining factor. Had it been ported to more operating systems earlier history could be different right now.
That was 40 years ago. If people wanted to program inside out with lots of nesting then unfold it in their head, they would have done it at some point a long time ago. It just isn't how people want to work.

People don't work in postfix notation either, even though it would be more direct to parse. What people feel is clearer is much more important.

It's not just Lisp, though. The prefix syntax was the original one when the concept of records/structs were first introduced in ALGOL-like languages - i.e. you'd have something like `name(manager(employee))` or `name OF manager OF employee`. Dot-syntax was introduced shortly after and very quickly won over.
De gustibus non disputandum est, I personally find the C++/Java/Rust/... style postfix notation (foo.bar()) to be appalling.
TXR Lisp has this notation, combined with Lisp parethesis placement.

Tather than obj.f(a, b). we have obj.(f a b).

  1> (defstruct dog ()
       (:method bark (self) (put-line "Woof!")))
  #<struct-type dog>
  2> (let ((d (new dog)))
       d.(bark))
  Woof!
  t
The dot notation is more restricted than in mainstream languages, and has a strict correspondence to underlying Lisp syntax, with read-print consistency.

  3> '(qref a b c (d) e f)
  a.b.c.(d).e.f
Cannot have a number in there; that won't go to dot notation:

  4> '(qref a b 3 (d) e f)
  (qref a b 3 (d)
    e f)
Chains of dot method calls work, by the way:

  1> (defstruct circular ()
       val
       (:method next (self) self))
  #<struct-type circular>
  2> (new circular val 42)
  #S(circular val 42)
  3> *2.(next).(next).(next).(next).val
  42
There must not be whitespace around the dot, though; you simply canot split this across lines. In other words:

   *2.(next)
   .(next) ;; nope!
   .(next) ;; what did I say?
The "null safe" dot is .? The following check obj for nil; if so, they yield nil rather than trying to access the object or call a method:

  obj.?slot
  obj.?(method arg ...)
And what about when `bar` takes several inputs? Postfix seems like an ugly hack that hyper-fixates on functions of a single argument to the detriment of everything else.
It's not like postfix replaces everything else. You can still do foo(bar, baz) where that makes the most sense.

However, experience shows that functions having one "special" argument that basically corresponds to grammatical subject in natural languages is such a common case that it makes sense for PLs to have syntactic sugar for it.

Look at the last line in the example, where I show a method being called on a tuple. Postfix syntax isn't limited to methods that take a single argument.