While awful provides a vast assortment of built-in functionality, an indispensable part of any programming language is the possibility to define new functions. Functions can be used as a mean to avoid code duplication - instead of copy-pasting fragments of code, one can put then into functions, and then just invoke those routines. It also provides a possibility to shorten certain expressions, improving their readability.
This article focuses on user-defined functions in awful.
!fun :delta $a $b $c :pow &b i2 :mul &a &c i4 :return :sub $b $a !nuf :writeln s'4x*x + 2x + 5' :writeln s'Delta: ' :delta i4 i2 i5
Defining a function is done using the !fun
construct. It is named as such because, first: it's an abbreviation for pfunctionp, second: defining functions sure is fun. As can be seen from the example, the !fun
construct should be followed by the function name, and then, a list of arguments. Arguments can be defined using either the dollar ($
) or the ampersand (&
) symbol - as of v.0.4.4, there is no difference between these two declarations. In the example above, function :delta
is defined; it takes three arguments.
After the !fun
construct, function code follows. The end of in-function code is marked using the !nuf
construct. Sub-functions are not supported. Nesting function declarations will cause a fatal error.
User-defined functions in awful can be called just the same way built-in functions are called - just slap a :functionName
somewhere in the code and you're set. There are, however, two rules to remember:
:writeln s'cube CALL = ' :call s'cube' f+3.0 # :cube is undeclared here; explicit call would cause a parse error !fun :cube $NUM :return :pow $NUM i3 !nuf :writeln s'cube NAME = ' :cube f-3.5 # :cube has been declared, so this will work #~ This declaration would cause a parse error due to collision with built-in function :not !fun :not $NUM :return :sub i0 $NUM !nuf ~#
The second restriction can, however, be avoided by using the :call
function. It expects the first argument to be a string containing the name of function to be called, and the remaining arguments are passed to the function invoked.
Aside from simply executing some code, functions can also be used as a mean to shorten an expression - by calculating the value in a separate function, we improve readability of the compound statement. Returning a value from a function is done using the :return
function - it simply propagates the leftmost argument it receives - which can be either the result of an expression, a variable, or a value literal. After invoking this routine, the user-defined function will exit immediately, and execution flow will return to the place where the userland function was called.
!fun :funny $wat !if $wat :return s'lolwut' !fi # No :return statement here! !nuf :writeln :funny i1 # Will print 'lolwut' :writeln :funny i0 # Will print '{NIL}'
When exiting the function by the !nuf
construct (simply reaching end of function code), NIL
is returned.
Each function call creates its own variable pool. When accessing a variable, either by the dollar ($
) or the ampersand (&
) symbol, the name is searched only among the highest-level (current level) variable pool, and the lowest-level (global) variable pool. If a function argument has the same name as a global variable, the argument will shadow it, making it inaccessible. If a local variable of given name is not found, a lookup in the global pool is performed; only then a new local variable is created.
!fun :scope $abc :set &abc i50 :set &pqr i50 :set &xyz i50 !nuf :set &abc i10 :set &pqr i10 :scope i15 :writeln $abc s'; ' $pqr s'; ' $xyz
In the example above, the :scope
function performs three assignments. The first assignment to &abc
modifies the function's local variable, leaving the global var unchanged. The second call modifies the global var, because after looking among local vars, the interpreter then checks the global var-pool. Lastly, the third assignment, after checking both local and global var-pools and not finding the variable name in either, will create a new local variable, and then use it to perform the assignment.
awful currently does not support function overloading - attempting to create a second user-defined function using the same name will cause a fatal error. However, if you're willing to put in some extra work, it is possible to create a function taking various numbers of parameters.
!fun :avg :set &arr :func-args :set &avg f0.0 :set &limit :arr-count &arr :set &i $limit !while $i :add &avg $arr[:sub &i i1] !done :return :div $avg $limit !nuf :writeln :avg i10 i12 i33 :writeln :avg i10 i31 i52 i72 i95
The example above demonstrates the of the :func-args
function. It returns an array containing all the parameters passed to the function, with indexes starting from 0. The example above simply calculates the average value of provided arguments, but one could also use an !if
to check the value count of the argument array and perform different kind of operations depending on it.
Variable references can be passed to user-defined functions just like to built-in functions, and will work similarly.
!fun :twoHalf $a $b :mul &a i5 :mul &b i2 :return :div $a $b !nuf :set &a &b i10 :writeln $a s- - $b :writeln :twoHalf $a $b :writeln $a s- - $b :writeln :twoHalf &a &b :writeln $a s- - $b
When a variable is passed to a function by-value (using the dollar $
symbol), a copy of the original value is created and passed to the function. Whatever changes the function will make to the local variable, the original remains unchanged. When a variable is passed by reference (using the ampersand &
symbol), the function receives the original variable and any in-function operations will alter its value.
Note that it is currently impossible to use variable references with :func-args
, as arrays are always copied by-value and references are not preserved.
wikistrona zmodyfikowana 2014/0601/2317