Casts and coercions

Casts and coercions

Type indexing SEXP’s makes it possible to precisely characterize the set of values that a function can accept as argument or return as a result, but this only works well when the forms of R values are known a priori, which is not always the case. In particular, the type of the result of a call to the r quasiquoter is always SomeSEXP, meaning that the form of the result is not statically known. If the result needs to be passed to a function with a precise signature, say SEXP s R.Real -> SEXP s R.Logical, one option is to discover the form of the result, by first performing pattern matching on the result before passing it to the function:

f :: SEXP s R.Real -> SEXP s R.Logical

g = do SomeSEXP x <- [r| 1 + 1 |]
       case x of
         (hexp -> R.Int v) -> return (f x)
         _ -> error "Not an int."

But pattern matching in this manner can be verbose, and sometimes the user knows more than the type checker does. In the example above, we know that [r| 1 + 1 |] will always return a real. We can use casts or coercions to inform the type checker of this:

f :: SEXP s R.Real -> SEXP s R.Logical

g = do x <- [r| 1 + 1 |]
       return $ f (R.SInt `R.cast` x)

A cast introduces a dynamic form check at runtime to verify that the form of the result was indeed of the specified type. This dynamic type check has a (very small) runtime cost. Note the type of cast:

cast :: SSEXPTYPE a -> SomeSEXP s -> SEXP s a

SSEXPTYPE a is a so-called singleton type family. To each SEXPTYPE corresponds a SSEXPTYPE a and vice versa: Int :: SEXPTYPE has SInt :: SSEXPTYPE Int, Char :: SEXPTYPE has SChar :: SSEXPTYPE Char, etc. The only point of using [singleton][hackage-singletons] types here is to make the type of the result of cast be determined by the type of its arguments.

If the user is extra sure about the form, she may use coercions to avoid even the dynamic check, when the situation warrants it (say in tight loops). This is done with

unsafeCoerce :: SEXP s a -> SEXP s b

This function is highly unsafe - it is inline-r’s equivalent to Haskell’s System.IO.Unsafe.unsafeCoerce. It is a trapdoor that can break type safety: if the form of the argument happens to not match the expected form at runtime then a segfault may result, or worse, silent memory corruption.