'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.

Reply via email to