Вы можете написать обертку следующим образом:
check.types = function(classes, func) {
n = as.name
params = formals(func)
param.names = lapply(names(params), n)
handler = function() { }
formals(handler) = params
checks = lapply(seq_along(param.names), function(I) {
as.call(list(n('assert.class'), param.names[[I]], classes[[I]]))
})
body(handler) = as.call(c(
list(n('{')),
checks,
list(as.call(list(n('<-'), n('.func'), func))),
list(as.call(c(list(n('.func')), lapply(param.names, as.name))))
))
handler
}
assert.class = function(x, cls) {
stopifnot(cls %in% class(x))
}
И использовать ее как
f = check.types(c('numeric', 'numeric'), function(x, y) {
x + y
})
> f(1, 2)
[1] 3
> f("1", "2")
Error: cls %in% class(x) is not TRUE
Сделано несколько неудобно из-за того, что R не имеет декораторов.Это отчасти хакерство и страдает от некоторых серьезных проблем:
Вы теряете ленивую оценку, потому что вы должны оценить аргумент, чтобы определить его тип.
Вы все еще не можете проверить типы до времени звонка;настоящая статическая проверка типов позволяет проверять типы даже вызова, который на самом деле никогда не происходит.
Поскольку R использует отложенную оценку, (2) может сделать проверку типа не очень полезной, поскольку вызовна самом деле может не произойти до очень позднего или никогда.
Ответом на (2) будет добавление статической информации о типе.Вероятно, вы могли бы сделать это путем преобразования выражений, но я не думаю, что вы хотите туда идти.