Monday, September 10, 2012
Function “annotations” are a feature that many languages support. They may take various forms such as decorators in Python or class and method annotations in Java – having direct (like Python) or no direct (like Java) effect on the code it annotates. Factor has some similar features that I’ll demonstrate below.
We can define a simple word to use in this example (that adds
2 to its
: foo ( x -- n ) 2 + ;
Using the tools.annotations vocabulary, we can attach a “watch” annotation that prints the inputs and outputs of a word when it is called:
IN: scratchpad \ foo watch
If you print the word definition, you can see how it was modified:
IN: scratchpad \ foo see USING: math tools.annotations.private ; IN: scratchpad : foo ( x -- n ) \ foo entering 2 + \ foo leaving ;
You can call this word (either directly, or indirectly by calling another word which calls it) and see its inputs and outputs. A nice feature of this is that the UI lets you click on these values and see more detail (particularly useful if they are tuples or more complex objects):
IN: scratchpad 3 foo --- Entering foo x 3 --- Leaving foo n 5
You can stop watching a word by calling “reset” on it (or right-clicking on its definition in the UI and choosing “reset”) to remove all of its annotations. Currently, a word can only be annotated once.
IN: scratchpad \ foo reset
In addition to “watching”, you can also use annotations to track the total running time of a word:
IN: scratchpad \ foo add-timing IN: scratchpad 0 10,000 [ foo ] times . 20000 IN: scratchpad word-timing. foo 0.000594456
It also supports arbitrary annotations, such as adding “before” and “after” logic:
IN: scratchpad \ foo [ "hi" print ] [ "bye" print ] [ surround ] 2curry annotate IN: scratchpad 3 foo . hi bye 5
These annotations can be pretty powerful and were even used to build our code coverage tool.