'John Clements' via Racket Users wrote on 08/02/2018 01:24 PM:
Unfortunately, I’ve been on the other side of this fence, too: parameters are
vastly more convenient for implementors than adding optional parameters to
every one of the internal calls. This certainly came up for me in the
construction of a CSV writing library.
I can imagine a bunch of programming patterns that might assist this; the most
obvious one would be an object-like metaphor where parameter values are
represented as an object to which calls are made. Do others have ideas on a
“best practice” for this situation and others like it?
I appreciate your point, and, for the list to pick apart, I'd like to
state a perspective on some simple API (specified module interface)
conventions that's probably pretty familiar or obvious to Racketeers,
and how Racket parameters an objects might fit and not fit into that...
When practical, for careful engineering, and especially in reusable and
maintainable Racket code, I usually like when the API for a module is
made up of procedures (sometimes ideally, pure functions) that are easy
to test individually without any/much context. And that have reasonable
defaults for arguments that are reasonable to be optional.
Then, to make that somewhat-careful API also be a
quick/convenient/little-languages API, one of the measures that
sometimes makes sense is to have some optional arguments default to the
values of Racket parameters, especially ones that can be set through the
API. (This is one way Racket parameters can be used, but not the only
way.) When one one of these optional arguments results in a Racket
parameter's value being gotten by the API implementation, the API
implementation then passes around that value as explicit arguments. The
API implementation internally doesn't use optional arguments much, nor
does it use Racket parameters much.
Separate from whether a value to the API is coming from a Racket
argument or parameter, we might group some of the arguments together in
what I'll call structs at the moment[1]. This grouping might be because
it's a natural identified object from an analysis of the problem
domain/context (e.g., conventions of CSV file), or from concepts
introduced in the design of the software/hardware system relative to the
analysis, or from the lowest, code-level details/decisions.[2]
In addition to code-level API adding concepts not present at
analysis-level, it can also remove them. For a simple example, API
functions that simply have separate arguments for X and Y -- even though
"2D point" might be a natural object in the problem domain -- for
whatever reason of programmer convenience or implementation efficiency,
When you have these structs (or objects/tuples/whatever) as arguments to
API functions, for whatever reason, the API can choose to treat them
just we did non-struct optional arguments, defaulting to the values of
Racket parameters. If that makes more sense than having fixed defaults.
At the moment, I can't think of other common good uses for Racket
parameters in idiomatic Racket, though I'm sure they exist (especially
for implementing different `#lang`s that are not idiomatic Racket).
And these conventions also mean that a given use of an API can choose to
use arguments rather than Racket parameters, rather than being required
to use them.[3]
Of course, sometimes a system really wants to be decomposed differently,
such as with objects that have subclassing and/or composition, and
behavior is distributed around very differently, and function-oriented
conventions don't necessarily apply. But the primary use of Racket
parameters in APIs of those models might still be providing "defaults"
for things that are optional to specify but might not have fixed
defaults.[4]
I don't think I said much here, but it's a particular not-much, :) which
I suppose might preclude some other API design problems and eventual
questions.
[1] I'll say "struct" here, like an unambiguous Racket `struct`, since
"object" can not only be confused with Racket `object`, but can imply
big other things about the overall design of the API (e.g., full-blown
pure OO distribution of behavior to the objects themselves, and objects
that might arise from associations and interaction between objects...
which could be very different from a more function-oriented decomposition).
[2] Different methodologies and schools of thought conceptualize these
"levels" differently. I'm using them in a vague, general way here.
[3] At least they can choose to use or not use Racket parameters in
their use of the immediate API. Though that API might be implemented
using other APIs that force parameters upon it, and some parameter-ish
behavior might end up exposed through to users of this API, even though
the interface is not.
[4] Of course, whenever you might have these optional arguments
defaulting to who-knows-what from parameters, you want to be judicious.
As a related anecdote, the old `html-template`
("https://www.neilvandyke.org/racket/html-template/") package isn't a
good example of the convention, but it goes to trouble to mitigate some
problems of *other* code that inadvertently defaults to writing to
`current-output-port` parameter if an optional argument isn't given.
Specifically, in Web application server-side, you might be doing various
kinds of I/O, and, if you forget an output-port argument on just one of
those procedure calls, server might happily send that data as part of a
Web page HTML rather than to the service or storage you intended. And
there's ways that you can intentionally and correctly leave off the
optional argument when writing the code, but then later in the life of
the code, that's no longer correct. This seems to be a common enough
problem among some Web development that, if static analysis cannot tell
you of the error at compile time, `html-template` will try to give you a
runtime error. So you might want to be careful with new API, such as
(if you could go back decades in time) requiring that the output port be
mandatory for all general-purpose I/O operations, and then making
convenient separate procedures for a stdout/stderr/stdin textual user
interface that don't require it to be specified.
--
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.