From db8c29ce3204b79aed7b9679c91f7abf3f6f2102 Mon Sep 17 00:00:00 2001 From: Marion Zepf Date: Tue, 29 Oct 2013 21:25:26 +0000 Subject: convert to type branch of python export code --- (limited to 'doc/type-system.md') diff --git a/doc/type-system.md b/doc/type-system.md new file mode 100644 index 0000000..4615dd1 --- /dev/null +++ b/doc/type-system.md @@ -0,0 +1,116 @@ +The TA Type System +================== + +Why do we Need a Type System? +----------------------------- + +The purpose of the type system is to have a consistent and +standardized way of type-checking and type-converting the +arguments of blocks. For example, the 'minus' block takes two +arguments of type TYPE_NUMBER. But that doesn't mean that only +number blocks can be attached to its argument docks. In fact, +colors, single characters, and numeric strings (like `"-5.2"`) +can easily be converted to numbers. The type system takes care +of this. When e.g., a color is attached to the argument dock of +the 'minus' block, the type system tries to find a converter +from the type TYPE_COLOR (the type of the color block) to the +type TYPE_NUMBER. If it finds one (and in this case it does), +then the converter is applied to the argument. A converter is +simply a callable (usually a function) and applying it simply +means calling it and passing it the value of the argument block +as a parameter. The converter returns the number that cor- +responds to the color, and the number is passed on to the +'minus' block. This way, the 'minus' block does not have to know +about colors, characters, or numeric strings. Likewise, the +color block also does not have to care about how its value can +be converted to a number. + +Why do some Blocks Need Return Types? +------------------------------------- + +The argument to the 'minus' block (to continue our example) need +not be a simple value block; it can also be the result of a +complex mathematical operation, i.e. the return type of another +block such as 'multiply'. The 'minus' block still demands a +value of type TYPE_NUMBER, so the 'multiply' block must provide +information about its return type. This is why blocks that can +be used as arguments to other blocks must specify a return type. + +What if I want to Specify Two Types at the Same Time? +----------------------------------------------------- + +You can use the function `or_` (defined in `taprimitive.py`) to +create disjunctions of `Primitive`s, argument lists, `ArgSlot`s, +or types. Simply pass the disjuncts to it as its arguments. +E.g., to create a disjunction between the types TYPE_NUMBER and +TYPE_STRING, call + + or_(TYPE_NUMBER, TYPE_STRING) + +The return value of the `or_` function will in this case be a +`TypeDisjunction` object that holds the two types. It means the +same as 'TYPE_NUMBER or TYPE_STRING' in English. The `or_` +function knows automatically from the type of its arguments +which type of object it must return. + +What if it is Impossible to Predict the Return Type of a Block? +--------------------------------------------------------------- + +In the case of the 'box' block, for example, it is impossible to +predict what type it will return at runtime because one cannot +foresee what will be inside the box at runtime. (E.g., the box +contents could depend on input from the keyboard or camera.) +This is where the special type TYPE_BOX comes in handy. It +allows you to postpone the search for a type converter until the +type of the box contents is known. As soon as this is the case, +the type system will automatically apply the appropriate type +converter. + +How to Add a New Type +--------------------- + +To add a new type to the type system, you need to instantiate a +new `Type` object and store it in a constant whose name starts +with `TYPE_`. You would do this in `tatype.py`: + + TYPE_MYTYPE = Type('TYPE_MYTYPE', 99) + +The number argument to the `Type` constructor can have an +arbitrary value, as long as it is different from the value of +every other `Type` object. + +You also need to tell the type system how to recognize runtime +objects that belong to your type. Add one or several new `elif` +clauses to the `get_type` function. E.g., if you are defining a +new type for dictionaries, you would add the clauses + + elif isinstance(x, dict): + return (TYPE_DICT, False) + elif isinstance(x, ast.Dict): + return (TYPE_DICT, True) + +The second item of the tuple that `get_type` returns indicates +whether `x` is an AST (Abstract Syntax Tree) or not. Only +instances of subclasses of `ast.AST` are ASTs. + +Optionally, you can add converters for the new type. You can do +so by extending the dictionary `TYPE_CONVERTERS` in `tatype.py`. +The format is quite simple: To add a converter from your type to +e.g., TYPE_FLOAT, add the entry: + + TYPE_CONVERTERS = { + # ... + TYPE_MYTYPE: { + # ... + TYPE_FLOAT: float + # ... + } + # ... + } + +Note that it is not obligatory to use the function `float` as +the converter to the type TYPE_FLOAT. In fact, you can use any +function or method. Please make sure that the converter accepts +arguments of the source type (here, TYPE_MYTYPE) and returns a +value of the target type (here, TYPE_FLOAT). The converter must +not throw any errors. -- cgit v0.9.1