[Tech Note] Argument type checking

General Discussions about the API

[Tech Note] Argument type checking

Postby oliver » Fri Jul 27, 2012 6:31 am

Argument type-checking is added to functions to ensure they will only operate on the types of data, they're designed to operate on.

Here's how you go about type checking in MorphEngine:
Code: Select all
function(x, range) {
    if (calculator.typeOf(range) != "vector")
        throw Error("wrong type of argument");
    if (range.length != 3)
        throw Error("bad arg");
    ...
}


Note the use of throw Error. The two strings given are special: there're translated into all languages supported by the calc. The thrown error string is presented to the user as a dialog.

Also note how first the type is checked, and then the value of the argument.

The list of possible types is: "undefined" (JS undefined type), "number" (a real), "matrix", "vector", "expression or name", "complex number", "RPL program" (≪ ... ≫), "string" (a ND1 string that is a JS string that includes double-quotes inside the string), "boolean" (true, false), "binary number" (e.g., 1010b or 238o), "URL" (http://...), "data URL" (data:image), "object" (JS Object), "function" (JS function), "operator" (the objects inside a vector like [+ sin]), plus any extension-defined types. These include "fraction" (Fraction), "CF" (ContinuedFractionType), "bignum" (BigInt), "bigfloat" (BigFloat), "img" (NDImage), "tagged" (TaggedObject), "code" (Code; e.g.: /* some JS */ ...), "chem" (ChemFormula object), "GS" (GolfScript object), "color" (Color object).

The string returned by calculator.typeOf() is exactly what @typeof returns in interactive usage and in RPL.

Using this function is the cleanest and most coherent way to check for types.

There's also a number of functions defined on the calculator object, that you can use to identify types or certain patterns in strings:
Code: Select all
isAMatrix
isAVector
isADecimalNumber
isASmallInteger
isALikelyLiteral
isAComplexNumber
isABinaryNumber
isAHexNumberString
isAnOctNumberString
isATrueBinaryNumberString
isABoolean
isADataURL
isAnObjectString
isAFunctionString
isANaturalMathFunctionString
isAnRPLProgram
isAName
isALikelyExpression
isAFirmString


Internal functions often use calculator.isAVector() and calculator.isAMatrix() and calculator.isAFirmString() to check for "vector", "matrix" and "string", respectively, because these are somewhat faster than calculate.typeOf().


If you inject code into an existing function collection, you don't need to do type checking, because the framework ensures your function is called with the correct type.
For example, the stock function for complex.coth(), living in calculator.functions.complex (aka ME.complex), looks like this:
Code: Select all
   "coth": function(c) { return this["/"](this["cosh"](c), this["sinh"](c)); },

There no type checking, as the single argument c is guaranteed to be a complex number. Or else, the function wouldn't have been called. Under normal operation, that is. If someone calls ME.complex.coth([4,5,6]) and provides an incompatible argument to the function, it will behave in unpredictable ways. But a user, with normal calculator operation or RPL code, cannot produce a condition like that.

The same goes for functions defined on a custom type.
Here's, for example, BigFloat.isInt():
Code: Select all
   "isInt": function(a) { return BigFloat["=="](a, a.intPart()); },

The argument a is sure to be a "bigfloat". (More precisely: the kind of object that BigFloat will produce when you have it create an instance.)

The modern type name is part of the required properties of a custom type object.
For example, the BigInt class/type begins like this:
Code: Select all
var BigNum = {
   type: "bignum",
   typeHP: 28,
   ...

Hence, the modern name is "bignum" (and 28 is the classic code returned by TYPE).

If a function takes multiple arguments, only one argument is known to match the type of the function family (or custom type).
Here's ME.vector.zip():
Code: Select all
   "zip": function(x, y) {
      if (!(calculator.isAVector(x) && calculator.isAVector(y)))
         throw Error("wrong type of argument");
      if (x.length != y.length) throw Error("bad arg");
      var z = []; for (var i=0; i<x.length; i++) { z.push(x[i]); z.push(y[i]); } return z;
   },


Of course, checking for types is a vital ingredient in all those functions that do different things depending on the type of their arguments.
ME.vector["+"](), for example, ties many different actions to the various combinations of arguments it accepts. (Where unacceptable argument type combination still throw errors.)

Because "number" and "vector" are straight JS types, you may also use the usual techniques to identify those.
That is typeof x == "number" for number, and something like a instanceof Array or typeof x == "object" && length in x for array.


Type identification and arg checking in RPL is discussed here: http://forums.naivedesign.com/viewtopic.php?f=9&t=669
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

Return to General

Who is online

Users browsing this forum: No registered users and 1 guest

cron