Closed sorawee closed 2 months ago
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.
with-
and call-with-
conventionsFor 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
#%
conventionI 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.
-equal?
conventionFunctions 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 equalityAnd this should also include equal-now-hash-code
, equal-hash-code
, and identical-hash-code
, and corresponding hash-tables.
splicing-
conventionbegin
should be split into two forms:
splicing-begin
which splices in definition and body contextsbegin
which behaves like (let () body ...)
and removes the need for let-nil as a patternAs raised by https://github.com/racket/racket2-rfcs/issues/87
immutable-
and mutable-
conventionsImmutability 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.
-map
conventionvector-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-upcase
and string-downcase
char-upcase
and char-downcase
char-upper-case?
and char-lower-case?
A better set of names might be:
string-upper
, string-lower
char-upper
, char-lower
char-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/2
without 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.
Per @dedbox's suggestion
Naming Convention
?
boolean?
!
set!
%
game-state%
<%>
dc<%>
^
game-context^
@
testing-context@
#%
#%app
/
call/cc
make-
make-pipe
with-
with-limits
call-with-
with-
call-with-limits
current-
current-compile
=?
string=?
-ref
hash-ref
-set
hash-set
-set!
hash-set!
*
*
let*
/c
list/c
->
number->string
+
gen:equal+hash
gen:
gen:equal+hash
prop:
prop:procedure
define
define/contract
-values
define-values
in-
in-slice
splicing-
splicing-let
Discrepancies / Issues
?
: what about=
and<
? Should they be renamed to=?
and<?
(#16)?#%
: 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-lambda
can handle keyword arguments)?with-
:with-input-from-file
andwith-output-to-file
consume thunks rather than body (#54).collect-garbage
).splicing-begin
(#87)Let me know if I missed anything, and I will add it to the table.