> On Apr 10, 2018, at 14:00, David Storrs <david.sto...@gmail.com>
> wrote:
> 
> Aside from I/O, I can't think of too many cases where (void) is the
> intuitively correct or most useful return value, but it is extremely
> common throughout the built-in Racket functions.  I'm not sure where
> you're drawing the lines on 'API design' vs 'comprehensive
> guidelines', but I'd sure like it if the guideline "Always return
> something useful unless there's a really good reason not to" got
> included.

There is definitely a school of thought that buys into the idea of
returning “the thing being operated on” rather than returning nothing
for side-effectful functions. I think this is most characterized by
so-called “fluent interfaces”[1], a way of encoding DSLs into
object-oriented languages. As far as I can tell, this was style has been
around for a very long time, but it was really forced into mainstream
usage by the runaway success of jQuery in the mid to late aughts.

The advantage of fluent interfaces is significant if you have an API
that involves frequent object creation and mutation. It makes it
possible to program in a more expression-oriented style, similar to the
way the GoF builder pattern is useful in C++/Java-style OO languages.
However, it has a cost of imprecision: it’s not always clear which thing
is the most obvious to return, and it masks when functions exist solely
for side-effects. For an example of the first problem, consider the
jQuery $.append function[2]:

    $('.foo').append($('<p>Hello!</p>'))

Which element does this return? Does it return the set of elements
produced by $('.foo'), or does it return the new element created by
$('<p>Hello!</p>')? Both answers are useful, and indeed, jQuery actually
includes a separate $.appendTo function[3] that does the exact same
thing as $.append but flips the arguments around, mostly to make the
method chaining work out more nicely in certain situations. This is an
awkward thing for an API designer to worry about; it is confusing for a
library to provide the exact same function that just happens to return
a different one of its arguments.

The other problem with always returning something is that returning
#<void> is extremely meaningful: it means the function’s only purpose
is to perform a side-effect. When contracts (or, in a statically typed
language, types) are used precisely, they can be quite communicative
without having to read anything but the signatures alone. When given a
Racket function with the following signature:

    (-> vector? exact-integer? any/c void?)

...it’s pretty likely that function is vector-set!. But now imagine the
same function returned the mutated vector:

    (-> vector? exact-integer? any/c vector?)

Now it’s much less immediately clear that this function is intended to
be used to perform a side-effect, and I might misinterpret it as
returning a new vector instead of updating the existing one.

You might argue that the benefit in chaining outweighs the cost of
signature clarity, but I think Racket mostly eschews that idea because
Racket is a language with a functional bent. It discourages using
mutability where immutable structures will do, and of course, useful
functions on immutable data cannot return #<void>. Therefore, it’s both
(1) rare for idiomatic Racket to use lots of functions that produce
#<void>, so they wouldn’t benefit much from threading arguments through,
and (2) especially important that side-effectful functions are called
out as such as efficiently as possible.

Racket makes it easy to use the same value twice without forcing library
authors to arbitrarily pick certain arguments to thread through
side-effectful functions. Internal definition contexts are available
almost everywhere, and there is a plethora of local binding forms.
Ultimately, the choice to return #<void> instead of some input argument
probably doesn’t dramatically help or harm the language (it would still
be Racket if it aligned with the other school of thought), but I happen
to like the choice Racket makes.

Alexis

[1]: https://en.wikipedia.org/wiki/Fluent_interface
[2]: http://api.jquery.com/append/
[3]: http://api.jquery.com/appendTo/

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to