Using Nasal functions: Difference between revisions

Jump to navigation Jump to search
m
restructure, improve, etc.
m (restructure, improve, etc.)
Line 64: Line 64:
To add a "function body", you need to add code in between these curly braces. This code can pretty much be anything at all, and in addition to using global variables and local ones that it has defined, the code can use local variables that represent the function arguments.
To add a "function body", you need to add code in between these curly braces. This code can pretty much be anything at all, and in addition to using global variables and local ones that it has defined, the code can use local variables that represent the function arguments.


== Anonymous function arguments ==
== Returning from functions ==
 
In Nasal, functions return implicitly the values of the last expression (e.g. "nil" in empty function bodies), and you can also add an explicit "return" statement, for example to leave a function early. In addition, it is possible to return values, too. Whenever a function's signature is empty, because it doesn't take any named arguments, the parentheses are optional. The terminating semicolon after a func block is always optional:
 
So, semantically, these are all equivalent:
 
<syntaxhighlight lang="php">
var log_message = func() {};
 
var log_message = func() {}
 
var log_message = func {return;} # the semicolon here is also optional
 
var log_message = func {nil;} # and here too, of course
 
var log_message = func {};
 
var log_message = func return; # look, no braces! the function ends at the semicolon
 
var log_message = func nil;
 
var log_message = func() nil;
 
var log_message = func; # emptiness is equivalent to "nil"
</syntaxhighlight>
 
== Defining function arguments ==
 
As shown above, the function arguments belong in parentheses after the "func" keyword. There are some different semantics available for defining and customizing these arguments.
 
=== Anonymous function arguments ===


In Nasal, arguments are by default implicitly passed in the "arg" array, not unlike perl. To understand how this works, you should probably first read up on Nasal vectors, which is just a fancy word for a list of things, that can be individually addressed by appending an index number in square brackets:
In Nasal, arguments are by default implicitly passed in the "arg" array, not unlike perl. To understand how this works, you should probably first read up on Nasal vectors, which is just a fancy word for a list of things, that can be individually addressed by appending an index number in square brackets:
Line 74: Line 104:
</syntaxhighlight>
</syntaxhighlight>


Note that this is equivalent to:
Note that this is basically equivalent to:


<syntaxhighlight lang="php">
<syntaxhighlight lang="php">
Line 89: Line 119:
Also, Nasal being a functional programming language, all passed arguments will be local to the corresponding scope. If you want to modify state in a function, you'll preferably return new state to the caller.
Also, Nasal being a functional programming language, all passed arguments will be local to the corresponding scope. If you want to modify state in a function, you'll preferably return new state to the caller.


==Named function arguments==
=== Named function arguments ===
You can also pass named arguments to a function, thus saving the typing and performance costs of extracting them from the arg array:
You can also pass named arguments to a function, thus saving the typing and performance costs of extracting them from the arg array:


Line 100: Line 130:
The list of function arguments is called a function's "signature".
The list of function arguments is called a function's "signature".


== Default values for function arguments ==
=== Default values for function arguments ===


Function arguments can have default values, as in C++ or Python. Note that the default value must be a constant (number, string, function, or nil) and not a mutable composite object (list, hash).
Named function arguments can have default values, as in C++ or Python. Note that the default value must be a constant (number, string, function, or nil) and not a mutable composite object (list, hash).


<syntaxhighlight lang="php">
<syntaxhighlight lang="php">
Line 133: Line 163:
</syntaxhighlight>
</syntaxhighlight>


== Returning from functions ==
== Calling a function with arguments ==


In Nasal, functions return implicitly the values of the last expression (e.g. "nil" in empty function bodies), and you can also add an explicit "return" statement, for example to leave a function early. In addition, it is possible to return values, too. Whenever a function's signature is empty, because it doesn't take any named arguments, the parentheses are optional. The terminating semicolon after a func block is always optional:
At the very beginning, you saw that function arguments, when ''calling'' a function, go inside the parentheses after the function name. No arguments means nothing there. As with defining arguments, there are still a few ways to specify the arguments. Note that you can only use one ''or'' the other: unlike Python, the two methods do not mix well.


So, semantically, these are all equivalent:
=== Positional arguments ===


<syntaxhighlight lang="php">
The most commonly used method for specifying arguments is a simple comma-separated list of expressions, which get mapped to the named arguments, overflowing into the "arg" symbol if needed (or the argument declared with ... after it). Here's a series of examples illustrating this:
var log_message = func() {};
<!-- enclose="div" for wrapping -->
<syntaxhighlight lang="php" enclose="div">
# Repeated from above: a simple function with a defaulted argument
var log_message = func(msg="error") {
    print(msg);
}
log_message("Hello!"); # the first specified argument gets mapped to the first declared argument, thus: msg="Hello!"
log_message(); # no specified arguments, so the default is used: msg="error"


var log_message = func() {}
var print_arg = func {
 
    foreach (var argument; arg)
var log_message = func {return;} # the semicolon here is also optional
        print(argument);
 
}
var log_message = func {nil;} # and here too, of course
print_arg("Hello!", "How are you today?") # there are now two arguments and they are absorbed into the implicit "arg" vector, thus: arg=["Hello!", "How are you today?"]
 
print_arg() # no arguments, so arg=[] and nothing is done: print(argument) is not even run
var log_message = func {};
 
var log_message = func return; # look, no braces! the function ends at the semicolon
 
var log_message = func nil;
 
var log_message = func() nil;
 
var log_message = func; # emptiness is equivalent to "nil"
</syntaxhighlight>
</syntaxhighlight>


==Named arguments in function calls==
=== Named arguments ===
Nasal supports named function arguments in function calls, too.
Nasal supports named function arguments in function calls, too. As an alternative to the comma-separated list of ''positional'' function arguments, you can specify a hash literal in place of ordered function arguments, and it will become the local variable namespace for the called function, with variables named according to the hash indexes and with values according to the hash values.  This makes functions with many arguments - or obscure ones - more readable.
 
As an alternative to the comma-separated list of ''positional'' function arguments, you can specify a hash literal in place of ordered function arguments, and it will become the local variable namespace for the called function, with variables named according to the hash indexes and with values according to the hash values.  This makes functions with many arguments more readable.  


And it also makes it possible to call function's without having to take care of the right order of passing arguments, which comes in especially handy once you have to work with code that you haven't touched in a while. Basically, it's a really good idea to always make use of this whenever a function is non-trivial, in that it may take more than just 2 straightforward arguments.
And it also makes it possible to call function's without having to take care of the right order of passing arguments, which comes in especially handy once you have to work with code that you haven't touched in a while. Basically, it's a really good idea to always make use of this whenever a function is non-trivial, in that it may take more than just 2 straightforward arguments.
Line 169: Line 195:
<syntaxhighlight lang="php">
<syntaxhighlight lang="php">
#if we have functions defined:
#if we have functions defined:
var log_message = func (msg="") { #some code to log variable msg }
var log_message = func (msg="") {
var lookat =  func (heading=0, pitch=0, roll=0, x=nil, y=nil, z=nil, time=hil, fov=20) { #some code using those variables }
    #some code to log variable msg
}
var lookat =  func (heading=0, pitch=0, roll=0, x=nil, y=nil, z=nil, time=hil, fov=20) {
    #some code using those variables
}


#we can use them them the usual way with comma separated list of arguments:
#we can use them them the usual way with comma separated list of arguments:
log_message("Hello World!");
log_message("Hello World!");
lookat (180, 20, 0, XO, YO, ZO, now, 55);
lookat(180, 20, 0, XO, YO, ZO, now, 55);


#or we can use the hash literal arguments instead:
#or we can use the hash literal arguments instead:
Line 187: Line 217:
<syntaxhighlight lang="php">
<syntaxhighlight lang="php">
var setPosition = func (latitude_deg, longitude_deg, altitude_ft) {
var setPosition = func (latitude_deg, longitude_deg, altitude_ft) {
# do something here  
    # do something here
}
}
# the actual function call:
# the actual function call:
Line 196: Line 226:
This is a good practice, as you may eventually have to take a longer break, away from your code - and then even you yourself will come to appreciate such small things that make code more intuitive to work with.
This is a good practice, as you may eventually have to take a longer break, away from your code - and then even you yourself will come to appreciate such small things that make code more intuitive to work with.


Declared arguments are checked and defaulted as would be expected: it's an error if you fail to pass a value for an undefaulted argument, missing default arguments get assigned as usual, and any rest parameter (e.g. <tt>func(a,b=2,rest...){}</tt>) will be assigned with an empty vector.
Declared arguments are checked and defaulted as would be expected: it's an error if you fail to pass a value for a mandatory/undefaulted argument, missing default arguments get assigned as usual, and any rest parameter (e.g. <tt>func(a,b=2,rest...){}</tt>) will be assigned with an empty vector.


== Specifying parameters at call-time ==
=== Specifying parameters at call-time ===


The previously discussed syntax also makes it possible to have functions without any named function arguments in their signature, while specifying arguments only during invocation:
The previously discussed syntax also makes it possible to have functions without any named function arguments in their signature, while specifying arguments only during invocation:
Line 207: Line 237:
}
}


print_pos( lat:43.94, lon: 23.88);
print_pos( lat:43.94, lon: 23.88); # these show up perfectly fine as lat=43.94 and lon=23.88
</syntaxhighlight>
</syntaxhighlight>


Line 216: Line 246:
Also note that the standard arg vector, including its ellipsis form, is no longer available when using a hash to initialize the parameters of a function call.
Also note that the standard arg vector, including its ellipsis form, is no longer available when using a hash to initialize the parameters of a function call.


==Nested functions, implicit return ==
== Nested functions, implicit return ==
Also, Nasal functions can be easily nested, for example:
Also, Nasal functions can be easily nested, for example:


Line 234: Line 264:
Note that the add,sub,mul and div functions in this example do not make use of an explicit return statement, instead the result of each expression is implicitly returned to the caller.
Note that the add,sub,mul and div functions in this example do not make use of an explicit return statement, instead the result of each expression is implicitly returned to the caller.


Nasal functions that just consist of such simple expressions can also be further simplified to read:
Nasal functions that just consist of such simple expressions can also be further simplified by ommitting the braces:


<syntaxhighlight lang="php">
<syntaxhighlight lang="php">
var add = func(val1,val2) val1+val2;
var add = func(p1,p2) p1+p2;
</syntaxhighlight>
</syntaxhighlight>


Line 299: Line 329:
</syntaxhighlight>
</syntaxhighlight>


==Function closures==
== Function closures ==
Each time a <tt>func {...}</tt> expression is evaluated, a new function object is created that is bound both to the namespace that it was evaluated in and to the currently executing function, which in turn is bound to an outer namespace and possibly another function/closure. This creates a list of namespaces, or closures, that represents the different possible scopes for a symbol name: a symbol can either be resolved in the local namespace of the function (created while executing that function) or in one of the successive closures. Since a closure is just a reference to a hash (the namespace), an outer closure may still exist even if it isn't accessible from a global namespace or the local namespace of an executing function (think stack frames in C), since it is kept accessible by the reference from the function object. For example:
Each time a <tt>func {...}</tt> expression is evaluated, a new function object is created that is bound both to the namespace that it was evaluated in and to the currently executing function, which in turn is bound to an outer namespace and possibly another function/closure. This creates a list of namespaces, or closures, that represents the different possible scopes for a symbol name: a symbol can either be resolved in the local namespace of the function (created while executing that function) or in one of the successive closures. Since a closure is just a reference to a hash (the namespace), an outer closure may still exist even if it isn't accessible from a global namespace or the local namespace of an executing function (think stack frames in C), since it is kept accessible by the reference from the function object. For example:


Line 316: Line 346:
As evidenced by the example, a function can remember the local namespace of the function that created it (e.g. the local namespace of <tt>generator</tt>) and can also remember the same closures that the generator had (which in this example would be both the namespace that contains the outer variable as well as the global namespace, whatever that might be).
As evidenced by the example, a function can remember the local namespace of the function that created it (e.g. the local namespace of <tt>generator</tt>) and can also remember the same closures that the generator had (which in this example would be both the namespace that contains the outer variable as well as the global namespace, whatever that might be).


===Misuse of closures===
=== Misuse of closures ===


What's wrong this this code?
What's wrong this this code?
Line 354: Line 384:
(for the record, bind returns the function it just rebound, even though it works via mutation, so essentially it is a convenience return of the first argument.)
(for the record, bind returns the function it just rebound, even though it works via mutation, so essentially it is a convenience return of the first argument.)


==Functional programming, higher order functions, generators (advanced concept) ==
== Functional programming, higher order functions, generators (advanced concept) ==
As previously mentioned, arguments to a Nasal function can also be functions themselves (Nasal being a functional programming language), this means that Nasal functions are higher order functions so that you can easily pass and return functions to and from Nasal functions. This can for example be used to dynamically create new functions (such functions are commonly called 'generators'):
As previously mentioned, arguments to a Nasal function can also be functions themselves (Nasal being a functional programming language), this means that Nasal functions are higher order functions so that you can easily pass and return functions to and from Nasal functions. This can for example be used to dynamically create new functions (such functions are commonly called 'generators'):


395

edits

Navigation menu