5 Semantic names
Package wyz.code.offensiveProgramming enforces semantic naming to offer great functionalities. Semantic naming come at the cost of few conditions. It has to be used for
- function parameters names
- function return type definition
- test case definitions
5.1 What is semantic naming?
I call semantic naming, the fact that a named object used in the code should provide much more information than a dumb name.
5.1.1 Naming recommendation
Naming conventions are recommendations. You may follow them strictly or lazily. This has some consequences on the checks you will have to run as you will have to know under which mode you work.
Package wyz.code.offensiveProgramming defines a few naming recommendations to declare class, function, function parameters and code variable names. Table below shows them and exposes the underlying philosophy.
name category | philosophy | pattern to comply with |
---|---|---|
class | starts with upper-cased letter, camel-cased | [A-Z][a-ZA-Z0-9]* |
function | starts with lower-cased letter, camel-cased | [a-z][a-ZA-Z0-9]* |
function parameter | See below next chapter | |
code variable | snake-cased | [a-z]+([a-z]+)* |
5.1.2 Semantic naming
Semantic naming is required on function parameter names and on return type names. Each semantic name must comply with one of the following patterns.
- <variableNameCamelCase>_<typeInformation>_<lengthConstraint>
- <variableNameCamelCase>_<lengthConstraint>_
First pattern is to be used for monomorphic types. Second one for polymorphic types.
Monomorphic types are types that are homogeneous. A string, a double, a MyObject are good example of such monomorphic types. Pattern allows to not only express concisely the type, but also to express some length constraints if needed.
Polymorphic types are useful as soon as your input or output can take many types, according to your context. For example, a R function may return a double, or a warning, or an error. Polymorphic types always end with ’_’ in their names, to make them easy to identify.
5.1.2.1 Type part of semantic naming
The type information part of the pattern has to match one suffix of the recorded entries of the type factory.
5.1.2.2 Length part of semantic naming
The length constraint part of the pattern follows the PERL pattern ([1-9][0-9]*(l|m|n)?). Letters mean respectively less or equal, more or equal, 1 or n. The length constraint part is optional.
5.1.2.3 Examples of semantic names
Look at following table to get more intuitive traction of semantic names. That’s easy to understand and to use.
input name | meaning |
---|---|
x_s | an unconstrained vector of strings - might contain no entries |
x_s_3 | a vector of strings with 3 entries |
x_s_3l | a vector of strings with 3 or less entries |
x_s_3m | a vector of strings with 3 or more entries |
x_s_3n | a vector of strings with 3 entries or 1 entry |
flag_b_1 | a vector of booleans with 1 entry - a.k.a a boolean scalar |
z_ | an unconstrained polymorphic type vector named z - Nothing more is known about its content |
z_2_ | a polymorphic type vector named z of length 2 - Nothing more is known about its content |
It is often advised when programming to use meaningful variable names. That’s hold true with semantic naming, but is far less critical. Here I used x or z as variable names, without any sacrifice of semantic naming due to the patterns I used. Indeed, using more contextually meaningful names should have even been a greater approach.
5.2 Verifying names
5.2.1 Verifying a function parameter or return type declaration
The function getTypeDescription from the type factory turns your semantic name into a human readable definition. It is a convenient way to verify your definition matches your need. It is also used to generate documentation for function parameter names.
5.2.2 Verifying a class name
Simply use function verifyClassName. Remind to set parameter strict according to your compliance to semantic naming. Use TRUE for strict compliance, FALSE for mazy compliance.
5.2.3 Verifying a function name
Simply use function verifyFunctionName. Remind to set parameter strict according to your compliance to semantic naming. Use TRUE for strict compliance, FALSE for mazy compliance.
verifyFunctionName(c('alphaBetaGamma', 'AlphaBetaGamma', '.alphaBetaGamma'))
#> alphaBetaGamma AlphaBetaGamma .alphaBetaGamma
#> TRUE FALSE FALSE
verifyFunctionName(c('alphaBetaGamma', 'AlphaBetaGamma', '.alphaBetaGamma'),
strict = FALSE)
#> alphaBetaGamma AlphaBetaGamma .alphaBetaGamma
#> TRUE TRUE TRUE
5.2.4 Get naming balance from an R object
Use function verifyObjectNames to get the results of a full semantic naming compliance analysis of names for the provided object. This analysis tries both strict and lazy compliance to provide results.
source(system.file('code-samples/no-defs/Addition.R',
package = 'wyz.code.offensiveProgramming'))
verifyObjectNames(Addition())
#> $class_name_compliance
#> Addition
#> TRUE
#>
#> $function_name_compliance
#> addDouble addInteger addNumeric divideByZero generateError
#> TRUE TRUE TRUE TRUE TRUE
#> generateWarning
#> TRUE
#>
#> $parameter_name_compliance
#> function_name parameter_name name_compliance_check semantic_naming_check
#> 1: addDouble x_d TRUE TRUE
#> 2: addDouble y_d TRUE TRUE
#> 3: addInteger x_i TRUE TRUE
#> 4: addInteger y_i TRUE TRUE
#> 5: addNumeric x FALSE FALSE
#> 6: addNumeric y_n TRUE TRUE
#> 7: divideByZero x_n TRUE TRUE
#> 8: generateError <NA> TRUE TRUE
#> 9: generateWarning x_ TRUE TRUE
#>
#> $classname
#> [1] "Addition"
#>
#> $owns_function_return_type_information
#> [1] FALSE
#>
#> $owns_test_case_definitions
#> [1] FALSE
#>
#> $supports_strict_compliance
#> [1] FALSE
#>
#> $supports_lazy_compliance
#> [1] FALSE
#>
#> $can_be_typed_checked
#> [1] FALSE
#>
#> $is_function_fully_instrumented
#> [1] FALSE
#>
#> $missing_functions
#> [1] NA
#>
#> $is_test_case_fully_instrumented
#> [1] FALSE
#>
#> $missing_test_cases
#> [1] NA
#>
#> $sof
#> $sof$frt
#> [1] FALSE
#>
#> $sof$tcd
#> [1] FALSE
#>
#> $sof$instrumented_fn
#> NULL
#>
#> $sof$instrumented_tc
#> NULL
source(system.file('code-samples/both-defs/good/full/AdditionTCFIG1.R',
package = 'wyz.code.offensiveProgramming'))
verifyObjectNames(AdditionTCFIG1())
#> $class_name_compliance
#> AdditionTCFIG1
#> TRUE
#>
#> $function_name_compliance
#> addDouble addInteger addMultiDouble addMultiInteger divideByZero
#> TRUE TRUE TRUE TRUE TRUE
#> generateError generateWarning
#> TRUE TRUE
#>
#> $parameter_name_compliance
#> function_name parameter_name name_compliance_check semantic_naming_check
#> 1: addDouble x_d TRUE TRUE
#> 2: addDouble y_d TRUE TRUE
#> 3: addInteger x_i TRUE TRUE
#> 4: addInteger y_i TRUE TRUE
#> 5: addMultiDouble ... TRUE TRUE
#> 6: addMultiInteger x_i TRUE TRUE
#> 7: addMultiInteger ... TRUE TRUE
#> 8: divideByZero x_n TRUE TRUE
#> 9: generateError <NA> TRUE TRUE
#> 10: generateWarning x_ TRUE TRUE
#>
#> $classname
#> [1] "AdditionTCFIG1"
#>
#> $owns_function_return_type_information
#> [1] TRUE
#>
#> $owns_test_case_definitions
#> [1] TRUE
#>
#> $supports_strict_compliance
#> [1] TRUE
#>
#> $supports_lazy_compliance
#> [1] TRUE
#>
#> $can_be_typed_checked
#> [1] TRUE
#>
#> $is_function_fully_instrumented
#> [1] TRUE
#>
#> $missing_functions
#> [1] "none"
#>
#> $is_test_case_fully_instrumented
#> [1] TRUE
#>
#> $missing_test_cases
#> [1] "none"
#>
#> $sof
#> $sof$frt
#> [1] TRUE
#>
#> $sof$tcd
#> [1] TRUE
#>
#> $sof$instrumented_fn
#> function_name return_value
#> 1: addDouble x_d
#> 2: addInteger x_i
#> 3: addMultiDouble x_d
#> 4: divideByZero x_d
#> 5: addMultiInteger x_i
#> 6: generateWarning x_w
#> 7: generateError x_er
#>
#> $sof$instrumented_tc
#> function_name standard_evaluation type_checking_enforcement
#> 1: addDouble correct correct
#> 2: addDouble correct correct
#> 3: addDouble correct correct
#> 4: addDouble correct correct
#> 5: addDouble correct correct
#> 6: addDouble erroneous erroneous
#> 7: addDouble correct failure
#> 8: addDouble correct failure
#> 9: addDouble correct failure
#> 10: addInteger correct correct
#> 11: addInteger correct correct
#> 12: addInteger correct correct
#> 13: addInteger correct correct
#> 14: addInteger erroneous failure
#> 15: addInteger correct failure
#> 16: addInteger correct failure
#> 17: addInteger correct failure
#> 18: addInteger correct failure
#> 19: divideByZero correct correct
#> 20: divideByZero correct correct
#> 21: divideByZero correct correct
#> 22: generateWarning correct correct
#> 23: generateError failure failure
#> 24: addMultiDouble correct correct
#> 25: addMultiDouble correct correct
#> 26: addMultiDouble correct correct
#> 27: addMultiInteger correct correct
#> 28: addMultiInteger correct failure
#> 29: addMultiInteger correct correct
#> function_name standard_evaluation type_checking_enforcement
#> test_case
#> 1: <TestCaseDefinition>
#> 2: <TestCaseDefinition>
#> 3: <TestCaseDefinition>
#> 4: <TestCaseDefinition>
#> 5: <TestCaseDefinition>
#> 6: <TestCaseDefinition>
#> 7: <TestCaseDefinition>
#> 8: <TestCaseDefinition>
#> 9: <TestCaseDefinition>
#> 10: <TestCaseDefinition>
#> 11: <TestCaseDefinition>
#> 12: <TestCaseDefinition>
#> 13: <TestCaseDefinition>
#> 14: <TestCaseDefinition>
#> 15: <TestCaseDefinition>
#> 16: <TestCaseDefinition>
#> 17: <TestCaseDefinition>
#> 18: <TestCaseDefinition>
#> 19: <TestCaseDefinition>
#> 20: <TestCaseDefinition>
#> 21: <TestCaseDefinition>
#> 22: <TestCaseDefinition>
#> 23: <TestCaseDefinition>
#> 24: <TestCaseDefinition>
#> 25: <TestCaseDefinition>
#> 26: <TestCaseDefinition>
#> 27: <TestCaseDefinition>
#> 28: <TestCaseDefinition>
#> 29: <TestCaseDefinition>
#> test_case