[R-pkg-devel] tryCatch defensive programming guidance

Martin Maechler maechler at stat.math.ethz.ch
Wed Mar 1 22:01:16 CET 2017


>>>>> Berry Boessenkool <berryboessenkool at hotmail.com>
>>>>>     on Wed, 1 Mar 2017 14:52:10 +0000 writes:

    > Hi Glenn,


    > Better late than never:
    > couldn't you simply use try?

    > result <- try( log("a") )

    > The printing is horrible: people will think an error
    > occured (but the function didn't stop!)

    > I tend to use it like this (which may be totally
    > unintended):


    > res <- try(log("a"), silent=TRUE)
    > if(inherits(res, "try-error"))
    > {
    >   message("log failed: ",res,". Now continuing with res=0.")
    >  res <- 0
    > }

but if you ever looked:  try() is just a wrapper to tryCatch()
and using try(*, silent=TRUE)  is even closer to a pretty simple tryCatch(.)

Historically,  tryCatch() did not exist, but try() did.
So much of "old code" still has try() calls in it.

I consider try() as convenience function for interactive use but
would always use tryCatch() for new code in my packages.


    > See here for my version that captures errors/warnings/messages with call tracing:

    > https://www.rdocumentation.org/packages/berryFunctions/topics/tryStack

I'd recommend you switch to tryCatch(): It is more flexible and
more directly "configurable" than try() -- which also got some historical
flexibility by the "hack" (not functional programming)
of depending on  getOption("show.error.messages")  in the
default case  silent = FALSE  {needed to suppress other
packages' using  try(.) 's printing of error messages as if
*error*s inspite of the fact that they were caught.

.... and back to the OP:

I think that's your only small problem that you chose
  error = function(e) print(e)

because that prints "as if" you had an error.

Martin


    > Regards,

    > Berry


    > ________________________________
    > From: R-package-devel <r-package-devel-bounces at r-project.org> on behalf of Glenn Schultz <glennmschultz at me.com>
    > Sent: Saturday, February 25, 2017 15:50
    > To: R Package Development
    > Subject: [R-pkg-devel] tryCatch defensive programming guidance

    > All,

    > I have the following to create a class PriceTypes.  I use try catch on the function and it gives me the error

    > price <- tryCatch(PriceTypes(price = "100")
    > ,error = function(e) print(e)
    > ,warning = function(w) print(w))

    > <simpleError in PriceTypes(price = "100"): tail value is missing>
    >> 

    > I read the section on tryCatch and withCallingHandlers as well the manual but I am still not clear as to how to use tryCatch in the function below I tried
    > PriceTypes <- TryCatch(
    > function(){}
    > ), function(e) print(error)

    > but this is obviously wrong as it did not work.  My question can I use tryCatch in the function itself or only when I invoke the function.

    > Best Regards,
    > Glenn

    > #' An S4 class representating bond price
    > #'
    > #' This class is used to create and pass the price types reported to
    > #' investors and used in analytics. For example price is often reported as
    > #' decimal or fractions 32nds to investors but price basis (price/100) is
    > #' used to calculate proceeds and compute metrics like yield, duration, and
    > #' partial durations.
    > #' @slot PriceDecimal A numeric value the price using decimal notation
    > #' @slot Price32nds A character the price using 32nds notation
    > #' @slot PriceBasis A numeric value price decimal notation in units of 100
    > #' @slot PriceDecimalString A character the price using decimal notation
    > #' @exportClass PriceTypes
    > setClass("PriceTypes",
    > representation(
    > PriceDecimal = "numeric",
    > Price32nds = "character",
    > PriceBasis = "numeric",
    > PriceDecimalString = "character")
    > )

    > setGeneric("PriceTypes", function(price = numeric())
    > {standardGeneric("PriceTypes")})

    > #' A standard generic function get the slot PriceDecimal
    > #'
    > #' @param object an S4 object
    > #' @export PriceDecimal
    > setGeneric("PriceDecimal", function(object)
    > {standardGeneric("PriceDecimal")})

    > #' A standard generic function to set the slot PriceDecimal
    > #'
    > #' @param object an S4 object
    > #' @param value the replacement value of the slot
    > #' @export PriceDecimal<-
    > setGeneric("PriceDecimal<-", function(object, value)
    > {standardGeneric("PriceDecimal<-")})

    > #' A standard generic function to get the slot Price32nds
    > #'
    > #' @param object an S4 object
    > #' @export Price32nds
    > setGeneric("Price32nds", function(object)
    > {standardGeneric("Price32nds")})

    > #' A standard generic function to set the slot Price32nds
    > #'
    > #' @param object an S4 object
    > #' @param value the replacement value of the slot
    > #' @export Price32nds<-
    > setGeneric("Price32nds<-", function(object, value)
    > {setGeneric("Price32nds")})

    > #' A standard generic to get the slot PriceBasis
    > #'
    > #' @param object an S4 object
    > #' @export PriceBasis
    > setGeneric("PriceBasis", function(object)
    > {standardGeneric("PriceBasis")})

    > #' A standard generic to set the slot PriceBasis
    > #'
    > #' @param object A S4 object of type PriceTypes
    > #' @param value the replacement value of the slot
    > #' @export PriceBasis<-
    > setGeneric("PriceBasis<-", function(object, value)
    > {standardGeneric("PriceBasis<-")})

    > #' A standard generic to get the slot PriceDecimalString
    > #'
    > #' @param object A S4 object of the type PriceTypes
    > #' @export
    > setGeneric("PriceDecimalString", function(object)
    > {setGeneric("PriceDecimalString")})

    > #' A standard generic to set the slot PriceDecimalString
    > #'
    > #' @param object A S4 object of the type PriceTypes
    > #' @param value The replacement value of the slot
    > #' @export
    > setGeneric("PriceDecimalString<-", function(object, value)
    > {setGeneric("PriceDecimalString<-")})

    > setMethod("initialize",
    > signature("PriceTypes"),
    > function(.Object,
    > PriceDecimal = numeric(),
    > Price32nds = "character",
    > PriceBasis = numeric(),
    > PriceDecimalString = "character",
    > ...){
    > callNextMethod(.Object,
    > PriceDecimal = PriceDecimal,
    > Price32nds = Price32nds,
    > PriceBasis = PriceBasis,
    > PriceDecimalString = PriceDecimalString,
    > ...)
    > })
    > #' A method to extract PriceDecimal from slot of class PriceTypes
    > #'
    > #' @param object an S4 object of the type PriceTypes
    > #' @exportMethod PriceDecimal
    > setMethod("PriceDecimal", signature("PriceTypes"),
    > function(object){object at PriceDecimal})

    > #' A method to set PriceDecimal in slot of class PriceTypes
    > #'
    > #' @param object an S4 object of the typre PriceTypes
    > #' @param value the replacement value of the slot
    > #' @exportMethod PriceDecimal<-
    > setReplaceMethod("PriceDecimal", signature("PriceTypes"),
    > function(object, value){
    > object at PriceDecimal <- value
    > return(object)
    > })

    > #' A method to extract Price32nds from slot of class PriceTypes
    > #'
    > #' @param object an S4 object of the type PriceTypes
    > #' @exportMethod Price32nds
    > setMethod("Price32nds", signature("PriceTypes"),
    > function(object){object at Price32nds})

    > #' A method to set Price32nds in slot of class PriceTypes
    > #'
    > #' @param object an S4 object of the type PriceTypes
    > #' @param value the replacement value of the slot
    > setReplaceMethod("Price32nds", signature("PriceTypes"),
    > function(object, value){
    > object at Price32nds <- value
    > return(object)
    > })

    > #' A method to extract PriceBasis from slot of class PriceTypres
    > #'
    > #' @param object an S4 object of the type PriceType
    > #' @exportMethod PriceBasis
    > setMethod("PriceBasis", signature("PriceTypes"),
    > function(object){object at PriceBasis})

    > #' a method to set PriceBasis from slot of class PriceTypes
    > #'
    > #' @param object an S4 object of the type PriceTypes
    > #' @param value the replacement value of the slot
    > #' @exportMethod PriceBasis<-
    > setReplaceMethod("PriceBasis", signature("PriceTypes"),
    > function(object, value){
    > object at PriceBasis <- value
    > return(object)
    > })

    > #' A method to extract PriceDecimalString from slot of class PriceTypes
    > #'
    > #' @param object an S4 object of type PriceTypes
    > #' @exportMethod PriceDecimalString
    > setMethod("PriceDecimalString", signature("PriceTypes"),
    > function(object){object at PriceDecimalString})

    > #' A method to set PriceDecimalString from slot of class PriceTypes
    > #'
    > #' @param object an S4 object of type PriceTypes
    > #' @param value the replacement value of the slot
    > #' @exportMethod PriceDecimalString<-
    > setReplaceMethod("PriceDecimalString", signature("PriceTypes"),
    > function(object, value){
    > object at PriceDecimalString <- value
    > return(object)})

    > #' PriceTypes is a constructor function for the class PriceTypes
    > #'
    > #' @param price character the price in either
    > #' decimal notation (example "100.125") or 32nds notation (example "100-4")
    > #' @export PriceTypes
    > PriceTypes <- function(price){
    > PriceBasis = 100
    > Units = 32

    > if(mode(price) != "character") stop ("price must be a character")
    > if(is.na(strsplit(price, "\\.|\\-")[[1]][2]) == TRUE) stop (
    > "tail value is missing")

    > Convertto32nds <- function(Price = "character"){
    > #convert price to numeric value
    > Price = as.numeric(Price)
    > tail32nds = round(x = (Price - floor(x = Price)) * 32, digits = 4)
    > Price = paste(as.character(floor(x=Price)),
    > "-",
    > as.character(tail32nds),
    > sep = "")
    > return(Price)
    > }

    > ConverttoDecimal <- function(Price = "character", Units = numeric()){
    > SplitPrice = strsplit(as.character(Price), "-")
    > handle = as.numeric(SplitPrice[[1]][1])
    > TailDecimal = signif(as.numeric(SplitPrice[[1]][2])/Units,8)
    > TailDecimal = gsub("(^|[^0-9])0+", "\\1", TailDecimal, perl = TRUE)
    > Price = paste(as.character(handle),
    > as.character(TailDecimal),sep="")
    > return(Price)
    > }

    > ConverttoString <- function(PriceDecimal = numeric()){
    > sprintf("%.8f", PriceDecimal)
    > }

    > # Convert Price when entered as a decimal value
    > if(grepl(".", as.character(price), fixed = TRUE) == TRUE){
    > Price_Decimal = format(as.numeric(price), nsmall =2)
    > Price_32nds = Convertto32nds(Price = price)
    > Price_Basis = as.numeric(price) / PriceBasis
    > Price_Decimal_String = ConverttoString(
    > PriceDecimal = as.numeric(Price_Decimal))
    > }

    > if(grepl("-", as.character(price), fixed = TRUE) == TRUE){
    > Price_Decimal = ConverttoDecimal(Price = price, Units = Units)
    > Price_32nds = price
    > Price_Basis = as.numeric(Price_Decimal)/PriceBasis
    > Price_Decimal_String = ConverttoString(
    > PriceDecimal = as.numeric(Price_Decimal))
    > }

    > new("PriceTypes",
    > PriceDecimal = as.numeric(Price_Decimal),
    > Price32nds = Price_32nds,
    > PriceBasis = as.numeric(Price_Basis),
    > PriceDecimalString = Price_Decimal_String
    > )
    > }



More information about the R-package-devel mailing list