let bar_x = x.bar()
let quux_y = y.quux()
return (bar_x, quux_y, z).foo()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.
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 ...)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.