7 Running functions
Our goal is to run a R instrumented function, under a given evaluation mode. To do so, some prerequisites have to be met, prior to use some wyz.code.offensiveProgramming utilities to proceed to the function execution with context capture and human-readable feedback generation.
7.1 Prerequisites
To run a R function is has to be instrumented. Two requirements have to be met
- the function must comply with semantic parameter naming
- the function return type must be specified
7.2 Function return types definition verification
To verify function return types definition you may use the low level function verifyFunctionReturnTypesDefinition or the higher level one named retrieveFunctionReturnTypes. Indeed, this approach requires persistent instrumentation of the code.
7.3 Transient invocations
Transient means instrumentation is done dynamically and not persisted anywhere. Here is a typical case. That’s a convenient way to discover and to play with package wyz.code.offensiveProgramming.
7.3.1 Nominal case
To get the definition for ‘emo’ variable, please refer to 6.
h <- function(x_s) x_s
runTransientFunction(h, list('neonira'), emo$type, 'x_s')
#> $status
#> [1] TRUE
#>
#> $value
#> [1] "neonira"
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_s neonira TRUE good type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_s neonira TRUE good type in values
Semantically, function h takes a vector of strings as argument and returns a vector of strings. As provided parameter ‘neonira’ is a vector of type character, parameter_type_checks succeed. As returned value is a vector of type character, function_return_type_check also succeeds.
7.3.1.1 Wrong parameter type
Let’s change the provided argument from ‘neonira’ to pi value.
h <- function(x_s) x_s
runTransientFunction(h, list(pi), emo$type, 'x_s')
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 3.141593
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_s 3.141593 FALSE wrong type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_s 3.141593 FALSE wrong type in values
We now face a case where function specification is unchanged but provided argument is not complying to specification. Impact is parameter_type_checks failure and function_return_type_check failure.
7.3.1.2 Change expected return type
Let’s change the expected function return type, to be double x_d.
h <- function(x_s) x_s
runTransientFunction(h, list(pi), emo$type, 'x_d')
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 3.141593
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_s 3.141593 FALSE wrong type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_d 3.141593 TRUE good type in values
We now face a case where function specification is unchanged but expected return type is expected to be a double. Provided argument is not complying to specification. Impact is parameter_type_checks failure.
7.3.2 Prerequisite mismatch
What if function is not fulfilling prerequisites
g <- function(x) x # No semantic name compliance
runTransientFunction(g, list(pi), EvaluationMode(defineEvaluationModes()[3]), 'x_d')
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 3.141593
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x 3.141593 FALSE unknown suffix, [NA]
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_d 3.141593 TRUE good type in values
Impact is parameter_type_checks failure.
7.3.3 Object function call
What if the function you desire to call is an object function? In such a case, be sure to pass the object as first parameter of the list of parameters, as shown by example below, based on a S3 object. A priori, all kind of R objects are supported: S3, S4, RC, R6 and environment objects.
library(data.table)
source(file.path(system.file(package = 'wyz.code.offensiveProgramming'), 'code-samples',
'both-defs/good/partial', 'Addition_TCFI_Partial_S3.R'), encoding = 'UTF-8')
a <- Addition_TCFI_Partial_S3()
runTransientFunction(addInteger.Addition_TCFI_Partial_S3, list(a, 3L, 4L), emo$type, 'x_i')
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 7
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity
#> 1: object_o_1 <Addition_TCFI_Partial_S3> FALSE
#> 2: x_i 3 TRUE
#> 3: y_i 4 TRUE
#> message
#> 1: wrong length, was expecting [1] , got [3]
#> 2: good type in values
#> 3: good type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 7 TRUE good type in values
runTransientFunction(addInteger.Addition_TCFI_Partial_S3, list(NULL, 3L, 4L), emo$type, 'x_i')
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 7
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity
#> 1: object_o_1 FALSE
#> 2: x_i 3 TRUE
#> 3: y_i 4 TRUE
#> message
#> 1: wrong length, was expecting [1] , got [0]
#> 2: good type in values
#> 3: good type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 7 TRUE good type in values
print(ls())
#> [1] "a"
#> [2] "addDouble.Addition_TCFI_Partial_S3"
#> [3] "addInteger"
#> [4] "addInteger.Addition_TCFI_Partial_S3"
#> [5] "Addition"
#> [6] "Addition_TCFI_Partial_S3"
#> [7] "AdditionTCFIG1"
#> [8] "addNumeric"
#> [9] "addNumeric.Addition_TCFI_Partial_S3"
#> [10] "divideByZero.Addition_TCFI_Partial_S3"
#> [11] "em"
#> [12] "emo"
#> [13] "f"
#> [14] "ff"
#> [15] "fg"
#> [16] "fh"
#> [17] "g"
#> [18] "generateError"
#> [19] "generateError.Addition_TCFI_Partial_S3"
#> [20] "generateWarning.Addition_TCFI_Partial_S3"
#> [21] "generateWarning2"
#> [22] "generateWarning2.Addition_TCFI_Partial_S3"
#> [23] "h"
#> [24] "MathOperation"
#> [25] "mo"
#runTransientFunction(addInteger, list(a, 3L, 4L), emo$type, 'x_i')
7.4 Persistent invocations
Transient invocations are convenient but limited. Especially, when you create classes, they do not appear to be as friendly and useful as necessary. When dealing with your own class code, you may opt for an easier and more industrial approach that is class instrumentation. Prerequisite remains the same, but you may fulfill them much more easily by defining a variable named function_return_type in your class. Let’s see an example.
7.4.1 Nominal persistent case
source(system.file('code-samples/frt-defs/good/partial/AdditionFIPartial.R', package = 'wyz.code.offensiveProgramming'))
runFunction(AdditionFIPartial(), 'addInteger', list(1:3, 6:8), emo$type)
#> $status
#> [1] TRUE
#>
#> $value
#> [1] 7 9 11
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] TRUE
#>
#> $parameter_check
#> [1] TRUE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_i 1,2,3 TRUE good type in values
#> 2: y_i 6,7,8 TRUE good type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 7, 9,11 TRUE good type in values
We now face a case where function specification is expecting to sum integers. Provided arguments are complying to specification. Impact is parameter_type_checks success and function_return_type_check success.
7.4.2 Subtile change
Just change the function name. Now expectations brought by the function definition are not the same. This leads to a completely different result.
runFunction(AdditionFIPartial(), 'addDouble', list(1:3, 6:8), emo$type)
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 7 9 11
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] FALSE
#>
#> $parameter_check
#> [1] FALSE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_d 1,2,3 FALSE wrong type in values
#> 2: y_d 6,7,8 FALSE wrong type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_d 7, 9,11 FALSE wrong type in values
7.4.3 Call case with named and positional parameters
runFunction(AdditionFIPartial(), 'addInteger', list(y_i = 1:3, 6:8), emo$type)
#> $status
#> [1] TRUE
#>
#> $value
#> [1] 7 9 11
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] TRUE
#>
#> $parameter_check
#> [1] TRUE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: y_i 1,2,3 TRUE good type in values
#> 2: x_i 6,7,8 TRUE good type in values
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 7, 9,11 TRUE good type in values
Look at parameter_type_checks to ensure arguments are well associated.
7.4.4 Call case with ellipsis
runFunction(AdditionFIPartial(), 'addMultiDouble', list(1:3, 1:7), emo$type)
#> $status
#> [1] TRUE
#>
#> $value
#> [1] 34
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] TRUE
#>
#> $parameter_check
#> [1] TRUE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: ... 1,2,3 TRUE ellipsis matches all
#> 2: ... 1,2,3,4,5,6,... TRUE ellipsis matches all
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_d 34 TRUE good type in values
7.4.5 Second call case with ellipsis
runFunction(AdditionFIPartial(), 'addMultiInteger', list(1:3, 1:7, 0, floor(pi)), emo$type)
#> $status
#> [1] FALSE
#>
#> $value
#> [1] 32 33 34
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] FALSE
#>
#> $parameter_check
#> [1] TRUE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_i 1,2,3 TRUE good type in values
#> 2: ... 1,2,3,4,5,6,... TRUE ellipsis matches all
#> 3: ... 0 TRUE ellipsis matches all
#> 4: ... 3 TRUE ellipsis matches all
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 32,33,34 FALSE wrong type in values
Two issues here. Value 0 is double, not integer and floor function returns also a double. To get a correct results here is how to transform the call
runFunction(AdditionFIPartial(), 'addMultiInteger', list(1:3, 1:7, 0L, as.integer(floor(pi))), emo$type)
#> $status
#> [1] TRUE
#>
#> $value
#> [1] 32 33 34
#>
#> $mode
#> [1] "type_checking_enforcement"
#>
#> $function_return_check
#> [1] TRUE
#>
#> $parameter_check
#> [1] TRUE
#>
#> $parameter_type_checks
#> parameter_name parameter_value validity message
#> 1: x_i 1,2,3 TRUE good type in values
#> 2: ... 1,2,3,4,5,6,... TRUE ellipsis matches all
#> 3: ... 0 TRUE ellipsis matches all
#> 4: ... 3 TRUE ellipsis matches all
#>
#> $function_return_type_check
#> parameter_name parameter_value validity message
#> 1: x_i 32,33,34 TRUE good type in values