rhombus-prototype
rhombus-prototype copied to clipboard
Make an RFC for naming convention
Per @dedbox's suggestion
Naming Convention
| Naming convention | Description | Example |
|---|---|---|
Ends with ? |
predicates and boolean-valued functions | boolean? |
Ends with ! |
setters and field mutators | set! |
Ends with % |
classes | game-state% |
Ends with <%> |
interfaces | dc<%> |
Ends with ^ |
unit signatures | game-context^ |
Ends with @ |
units | testing-context@ |
Begins with #% |
kernel identifiers | #%app |
Has / |
"with" (a preposition) | call/cc |
Begins with make- |
generative constructors | make-pipe |
Begins with with- |
forms that have internal def body | with-limits |
Begins with call-with- |
function variants of with- |
call-with-limits |
Begins with current- |
parameters | current-compile |
Ends with =? |
equality functions | string=? |
Ends with -ref |
accessors (non-destructive) | hash-ref |
Ends with -set |
setters (non-destructive) | hash-set |
Ends with -set! |
setters (destructive) | hash-set! |
Ends with * |
variants of non-* |
let* |
Ends with /c |
contracts | list/c |
Has -> |
"to" | number->string |
Has + |
"and" | gen:equal+hash |
Begins with gen: |
generics | gen:equal+hash |
Begins with prop: |
properties | prop:procedure |
Begins with define |
definition forms | define/contract |
Ends with -values |
multiple values variants | define-values |
Begins with in- |
sequences | in-slice |
Begins with splicing- |
splicing forms | splicing-let |
Discrepancies / Issues
- Ends with
?: what about=and<? Should they be renamed to=?and<?(#16)? - Begins with
#%: what aboutquote,case-lambda,let-values? These are core forms that are not prefixed with#%. Should there be#%variants so that non-#%variants can support more features (e.g.,case-lambdacan handle keyword arguments)? - Begins with
with-:with-input-from-fileandwith-output-to-fileconsume thunks rather than body (#54). - Should there be a naming convention for effectful functions? (e.g.,
collect-garbage). - There should be
splicing-begin(#87)
Let me know if I missed anything, and I will add it to the table.
Cross-ref: #16, #47
Contracts often end with /c with the exception of those that end with of or begin with ->.
Functions that process values of a certain type usually have names that begin with that type. Functions that pass them through to another type in some canonical way are often given names of the form a-type->another-type.
Definition forms often begin with define-.
Getters that have corresponding setters often use the suffix -ref to correspond with -set! or -set.
The hash-equal? function is confusing because it doesn’t ask whether two hash-tables are equal, as you would expect from the conventions above. Instead it asks whether a single hash-table uses equal? as its key-comparison predicate.
For the #% prefix, it doesn’t go on all kernel forms, but it does go on all interposition point forms. These are forms that can be overridden by a module-language to take over the meaning of that form within a module. Because of this, the #%app, #%module-begin, #%datum, etc. forms in Racket are macros and do not come from the kernel.
@AlexKnauth I used the word "kernel" following https://docs.racket-lang.org/style/Textual_Matters.html#%28part._names%29. I guess it includes identifiers that the macroexpander treats specially.
Another issue related to this:
For sets, we have: set (which is immutable), mutable-set, weak-set.
For hash maps, we have: hash (which is immutable), make-hash, make-immutable-hash, make-weak-hash.
We should make them consistent.
(from #29)
I think a good next step for this issue would be to make a list of possible name changes for identifiers in racket/base, so we can test how these conventions would work out on actual code.
For the with- and call-with- conventions
For many of these renames from with- to call-with-, a macro version that accepts a body should be added if it doesn't exist already.
with-check-info* rename -> call-with-check-info*
with-default-check-info* rename -> call-with-default-check-info*
with-errors-to-browser rename -> call-with-errors-to-browser
with-input-from-bytes rename -> call-with-input-from-bytes
with-input-from-string rename -> call-with-input-from-string
with-output-to-bytes rename -> call-with-output-to-bytes
with-output-to-string rename -> call-with-output-to-string
with-input-from-url rename -> call-with-input-from-url (from picturing-programs, but maybe we could have something like this in racket2)
with-installer-window rename -> call-with-installer-window
with-intercepted-logging rename -> call-with-intercepted-logging
with-logging-to-port rename -> call-with-logging-to-port
with-module-reading-parameterization rename -> call-with-module-reading-parameterization
with-continuation-mark extend to allow internal definitions
with-immediate-continuation-mark should exist and allow a body with internal definitions
with-default-reading-parameterization should exist and allow a body
with-check-info* should be a macro-with-body version of call-with-check-info*
with-trusted-sandbox-configuration exist and allow a body
with-semaphore should exist and allow a body
For the #% convention
I believe #%plain-lambda, #%expression, #%variable-reference, #%require, #%provide, and #%declare should be renamed to remove the #% prefix, since they are not override-able like the interposition point macros. They don't act like hooks.
The interposition point macros should keep the #% prefix, such as #%app, #%datum, #%module-begin, #%top, #%top-interaction, #%dot, and #%namespaced.
I believe the #% convention should be separated from the "kernel form" meaning, because languages usually define their own non-kernel versions of the interposition point macros to override behavior. So currently most kernel forms don't actually use #% (and that shouldn't be changed), and most #% definitions are actually non-kernel interposition point definitions for languages.
For the -equal? convention
Functions using a comparison predicate as a suffix that aren't comparison predicates themselves should get out of the way and be renamed:
hash-equal? rename -> hash-uses-equal-key-comparison? or something
hash-eqv? rename -> hash-uses-eqv-key-comparison?
hash-eq? rename -> hash-uses-eq-key-comparison?
For actual equality functions, I like the concept of "equal now" vs "equal always" from Pyret as in https://github.com/racket/racket2-rfcs/issues/16#issuecomment-512060826 and https://www.pyret.org/docs/latest/equality.html, so:
equal? rename -> equal-now?
eqv? delete
eq? rename -> identical? or same-object?
Add a new predicate named equal? corresponding to "equal always" that uses structural equality (extensional) for immutable values, and reference equality (intensional, but at least looking through chaperones) for mutable values. So at the end of that we have:
equal-now?structural equality even on mutable objects that might be made un-equal in the futureequal?structural equality for immutable objects, guarantees that they will always be equalidentical?reference equality
And this should also include equal-now-hash-code, equal-hash-code, and identical-hash-code, and corresponding hash-tables.
For the splicing- convention
begin should be split into two forms:
splicing-beginwhich splices in definition and body contextsbeginwhich behaves like(let () body ...)and removes the need for let-nil as a pattern
As raised by https://github.com/racket/racket2-rfcs/issues/87
For the immutable- and mutable- conventions
Immutability should be the default, so:
make-hash rename -> make-mutable-hash
make-string rename -> make-mutable-string
build-string rename -> build-mutable-string
make-vector rename -> make-mutable-vector
build-vector rename -> build-mutable-vector
And of course make-hash should be changed to make an immutable hash, make-string should make an immutable string, and so on.
For the -map convention
vector-map should return an immutable vector
hash-map should return a hash of the same kind
dict-map should return a dict of the same kind
set-map should return a set of the same kind
free-id-table-map should return a free-id-table
bound-id-table-map should return a bound-id-table
perhaps the current set-map, hash-map, etc. operations should be renamed to set-map->list and hash-map->list, to express that they return lists instead of the original data type
There's also an inconsistency in char/string case manipulating functions:
string-upcaseandstring-downcasechar-upcaseandchar-downcase- but
char-upper-case?andchar-lower-case?
A better set of names might be:
string-upper,string-lowerchar-upper,char-lowerchar-upper?,char-lower?
As a non-Scheme/Racket programmer, or more specifically, a programmer comfort with C++ or python, I take this type of character usage as abusing. You can simply naming something without these character. E.g. for boolean?, you just type is_boolean, note you might want to use is-boolean but it will be ambiguous with variable is minus variable boolean, unless you want to enforce whitespace delimited tokens.
I would prefer whitespace-delimited tokens. Well, whitespace, parens, brackets, braces, quotes, and commas, like racket already does.
I like ?, -, /, >, and other characters in my identifiers as normal characters, not token delimiters. Whitespace is more readable and better style anyway
Python has been my primary language for nearly 10 years, and I have come to loath the fact that my identifiers are restricted. This is not just a cosmetic matter, it means that when converting between strings and identifiers (which happens in python and tends to happen in Racket even more frequently), you have to choose some conversion convention for how to make a safe identifier, many people (including myself) have come up with conventions that cannot be inverted. Aside from the fact that writing 1/2 without spaces is awful for readability (and thus that whitespace-plus delimitation promotes basic readability), splitting on operators no matter where they are is just lazy on the part of the language designer because it means that don't have to implement special parsing for operators as operators always have the highest precedence. We don't have to do that here and make users suffer for the rest of the life of the language.
writing
1/2without spaces is awful for readability
Agreed, though Racket already parses that as a literal rational number. The restriction on operators and whitespace is more about things like x/2 or 3/x. If the numerator and denominator are both literal numbers, the parser doesn't need to emit an operator call expression at all and can instead just emit a literal rational. Same thing goes for negation, e.g. -12 is fine but -x is not.
Another one: mcons -> mutable-cons (or mutable-pair, see #21) to be consistent with, say, mutable-set.
Struct instance constructors? They used to follow the make- convention but they haven't for a long time. (Circa Racket 5.0, maybe?)
Struct instance constructors? They used to follow the
make-convention but they haven't for a long time. (Circa Racket 5.0, maybe?)
I think it depends what the struct is used for. When bundling together plain data, I usually omit the make- prefix. But if the struct contains opaque behavior (such as functions) or mutable state, I usually keep the prefix. This helps me tell whether I'm working with pure values or with encapsulating objects.