Is it possible to test if a value is a function that can be called? I can test for null easily but after that I have no idea how to ensure the parameter passed in is actually a function?
void myMethod(funcParam)
{
if (funcParam != null)
{
/* How to test if funcParam is actually a function that can be called? */
funcParam();
}
}
void myMethod(funcParam) {
if(funcParam is Function) {
funcParam();
}
}
Of course, the call to funcParams() only works if the parameter list matches - is Function doesn't check for that. If there are parameters involved, one can use typedefs to ensure this.
typedef void MyExpectedFunction(int someInt, String someString);
void myMethod(MyExpectedFunction funcParam, int intParam, String stringParam) {
if(funcParam is MyExpectedFunction) {
funcParam(intParam, stringParam);
}
}
In your case, you want to check if the function can be called with zero arguments.
typedef NullaryFunction();
main () {
var f = null;
print(f is NullaryFunction); // false
f = () {};
print(f is NullaryFunction); // true
f = (x) {};
print(f is NullaryFunction); // false
}
If you just want to know that it is some function, you can test with ... is Function. All callable objects implement Function, but it is technically possible (though often not useful) to implement Function manually without actually being callable. It does make a kind of sense for objects that mock callability through noSuchMethod.
var f = () {};
print(f is Function); // 'true'
var x = (x){};
print(x is Function); // 'true'
Related
Suppose a function that accepts an array of addresses and looks like this:
function setVoters(address[] _inputAddresses) public ownerOnly {
// [...]
}
the same contract that uses the aforementioned function has a variable defined as a mapping:
mapping(address => bool) voter;
Is looping over the array and pushing it to the mapping considered the best option when it comes to gas consumption/expenses or would it be better if the function accepts one address and does the iteration from a given UI via some JavaScript functionality?
option a:
function setVoters(address[] _inputAddresses) public ownerOnly {
// [...]
for (uint index = 0; index < _inputAddresses.length; index++) {
voter[_inputAddresses[index]] = true;
}
}
vs
option b:
function setVoter(address _inputAddress) public ownerOnly {
// [...]
voter[_inputAddress] = true;
}
JavaScript would look like this
// loop condition starts here
await task.methods.setVoter(address[key]).send({
from: accounts[0]
});
// loop condition ends here
The best in terms of gas efficiency is option a, calling a function takes quite a bit of gas, so you would pay less if you did it all in one big tx rather than many small ones.
love how Dart treats function arguments, but cannot accomplish what should be a simple task:
void func( String arg1, [ Map args = {} ] ) {
...
}
get the error
expression is not a valid compile-time constant
have tried new Map() for example, with same error.
You have to use the const keyword :
void func( String arg1, [ Map args = const {} ] ) {
...
}
Warning : if you try to modify the default args you will get :
Unsupported operation: Cannot set value in unmodifiable Map
The default value must be a compile time constant, so 'const {}' will keep the compiler happy, but possibly not your function.
If you want a new modifiable map for each call, you can't use a default value on the function parameter. That same value is used for every call to the function, so you can't get a new value for each call that way.
To create a new object each time the function is called, you have to do it in the function itself. The typical way is:
void func(String arg1, [Map args]) {
if (args == null) args = {};
...
}
In ActionScript 3, is there a clean way to define a function that accepts an optional boolean argument ? As you may know, this is invalid :
public function test(param:Boolean = null):void {
trace(param);
}
This triggers the following error: VerifyError: Error #1102: Illegal default value for type Boolean. Since, Boolean is a primitive, I guess it makes sense that it cannot be set to null. The only workaround I found is to cast the parameter to an object :
public function test(param:Object = null):void {
trace(Boolean(param));
}
However, this does not feel very clean, particularly if you are developing libraries. ASDoc will generate API documentation that says the expected parameter is an Object whereas what is really needed is a Boolean.
Is there a better approach ?
When you say optional, I assume that you mean if there isn't a value supplied then something different should happen compared to if you had a default value of true or false.
You could make your own object to handle the three states that you need and maintain code readability by using a class like this:
public class Condition
{
private var _value:* = null;
public function Condition(initial:* = null)
{
value = initial;
}
public function set value(n:*):void
{
if(_value === null || _value === false || _value === true)
{
_value = n;
}
}
public function get value():*{ return _value; }
}
And then your function could be:
function test(param:Condition = null):void
{
if(param && param.value != null)
{
trace(param.value);
}
}
test( new Condition() );
test( new Condition(true) );
As you said Boolean can not be set to null value.
Therefore, you should specify a default value that is either true or false.
public function test(param:Boolean = false):void {
trace(param);
}
But because you need the third case where nothing is set, one option could be to accept any Object but throw an exception if it is not null and not a boolean:
public function test(param:* = null):void
{
if (param != null)
{
if ((param == true) || (param == false))
{
trace(Boolean(param).toString());
}
else
{
throw new CustomError("param should be a boolean");
}
}
else
{
// Do nothing
}
}
Note that this solution also accept objects or primitives that can be compared to true or false such as 0, 1, or [].
From the good suggestions and discussion above I think that, in a library scenario and for simplicity's sake, the best way remains to type the parameter as Object with a default value of null but to request a Boolean in the API documentation :
/**
* #param param Boolean object or null
*/
public function test(param:Object = null):void {
trace(Boolean(param));
}
This allow the user of the library to pass a either a Boolean or nothing at all. Thanks everyone.
There was a tonne of discussion on my previous answer, but this is the correct way to have a function that accepts one of three states. My previous answer attempted to retain the use of a Boolean value like you were requesting, but that is not the right way to go about it.
Create a class that defines three values:
class State
{
public static const EMPTY:int = -1;
public static const FALSE:int = 0;
public static const TRUE:int = 1;
}
Your function will accept an int (the type of each of the three properties within your State class). It will deal with the three possible values. You can use concise commenting to notify the developer of what thee values the function is expecting, referencing the State class. The default value can be -1 aka State.EMPTY.
/**
* Function description.
* #param state One of three states, defined by State.
*/
function test(state:int = -1):void
{
switch(state)
{
case State.EMPTY:
// No value given.
break;
case State.TRUE:
// True.
//
break;
case State.FALSE:
// False.
//
break;
default:
throw new ArgumentError("Unsupported value for test()");
break;
}
}
this is from Adobe docs:
package {
import flash.display.Sprite;
public class Array_filter extends Sprite {
public function Array_filter() {
var employees:Array = new Array();
employees.push({name:"Employee 1", manager:false});
employees.push({name:"Employee 2", manager:true});
employees.push({name:"Employee 3", manager:false});
trace("Employees:");
employees.forEach(traceEmployee);
var managers:Array = employees.filter(isManager);
trace("Managers:");
managers.forEach(traceEmployee);
}
private function isManager(element:*, index:int, arr:Array):Boolean {
return (element.manager == true);
}
private function traceEmployee(element:*, index:int, arr:Array):void {
trace("\t" + element.name + ((element.manager) ? " (manager)" : ""));
}
}
}
The problem is the Array class filter method. It works this way: you pass a function as an argument of filter and an array is returned based on the function you pass. The problem is that it seems you can't add any other parameter. So, if you must create (for example inside a for loop) 4 arrays from the same array and you want to use the same function, you can only test against a property of the class you must previously set to the value you want to test.
Is there any other way to add that parameter?
When filtering by a variable object properties in an array, I wrapped the filtering into another function:
protected function FilterByProperty(input_array:Array, extra_testing:Object):Array
{
function FilterFunction(element:Object, index:int, array:Array):Boolean
{
return element.property == extra_testing; // Arbitrary test
}
return input_array.filter(FilterFunction);
}
var filtered_array:Array = FilterByProperty(unfiltered_array, test_property);
You can use second parameter of filter() method to pass data to filter function. It will be avaliable as "this" variable. To do that first parameter must be a closure, not a class method.
var array:Array = [...];
var params:Object = {"param1": value1, "param2": value2};
var filterFunction:Function = function(item:*, index:int, array:Array):Boolean {
var param1 = this["param1"];
var param2 = this["param2"];
};
array.filter(filterFunction, params);
You want to use something like Delegates or function binding, or closures. Depending on your coding and terminology preferences. The idea behind all of them is that you create a dynamic function wrapper for the core "filter" function. That wrapper will have access to extra parameters that you pass. So, the first time you call it, you might go:
a.filter(Delegate.create(myFunc, param1));
and the next time:
a.filter(Delegate.create(myFunc, param2));
and you function would have something like this:
private function myFunc(item:*, index:Number, a:Array, param:Object=null):Boolean{}
A quick an dirty method is to just pass an inline function like this:
a.filter(
function(item:*, index:Number, a:Array):Boolean {
return myFunc(item,index,a,param1);
}
);
where param1 is passed using the closure created by the function definition.
I am trying to understand the way the AsyncToken works in actionscript. How can I call a remote service and ensure that a specific parameter is available in the result or fault event functions? I think it is the async functionality I want to use.
The following code will hopefully explain what I am trying to do. Feel free to modify the code block as your explanation.
Thanks.
public function testSerivceCall(data:Object, callBackCommand:String):void
{
// Assume callBackCommand == "FOO";
// How can I pass in callBackCommand as a parameter to the result or fault events?
// How do I create an async token here?
var remoteObject:RemoteObject;
remoteObject = new RemoteObject();
remoteObject.destination = "zend";
remoteObject.source = "MyService";
remoteObject.endpoint = "http://example.com/service";
remoteObject.test.addEventListener(ResultEvent.RESULT, _handleTestResult);
remoteObject.test.addEventListener(FaultEvent.FAULT, _handleTestFault);
remoteObject.test(data);
}
private function _handleTestResult( event:ResultEvent ) : void
{
// How do I get the async token value?
// How can I get the value of callBackCommand in this code block?
if (callBackCommand == "FOO")
{
// do something related to "FOO"
}
else
{
// do something else with the result event
}
}
private function _handleTestFault( event:FaultEvent ) : void
{
// How do I get the async token value?
// How can I get the value of callBackCommand in this code block?
}
An edit to make this question more clear:
Assume I make the following method call somewhere in my code:
testSerivceCall(personObject, "LoginCommand");
How do I get access to the actual string "LoginCommand" inside the _handleTestResult function block?
The reason I want to do this is because I want to dynamically call back certain functions and hand off the result data to specific commands that I know ahead of time when I am making the service call.
I am just having a time grokking the AsyncToken syntax and functionality.
I did not even need closures. I added a class as below which I called externally.
The call was like this:
public class MyClass
{
...
var adminServerRO:AdminServerRO = new AdminServerRO();
adminServerRO.testSerivceCall("FOO",cptyId);
}
public class AdminServerRO
{
private function extResult( event:ResultEvent, token:Object ) : void
{
//the token is now accessed from the paremeter
var tmp:String = "in here";
}
private function extFault( event:FaultEvent ) : void
{
var tmp:String = "in here";
}
public function testSerivceCall(callBackCommand:String, cptyId:String):void
{
var remoteObject:RemoteObject = new RemoteObject();
remoteObject.destination = "adminServer";
var token:AsyncToken = remoteObject.getCounterpartyLimitMonitorItemNode(cptyId);
token.addResponder(new AsyncResponder(extResult,extFault,cptyId));
}
}
While the accepted answer will accomplish what the original submitter wants it does not actually answer the question which was asked. An AsyncToken is created as a result of a remote method call and is accessible from the ResultEvent. Since AsyncToken is a dynamic class you can add whatever property to it that you want. The code below should demonstrate this:
public function testSerivceCall(data:Object, callBackCommand:String):void
{
var remoteObject:RemoteObject;
remoteObject = new RemoteObject();
remoteObject.destination = "zend";
remoteObject.source = "MyService";
remoteObject.endpoint = "http://example.com/service";
remoteObject.test.addEventListener(ResultEvent.RESULT, _handleTestResult);
remoteObject.test.addEventListener(FaultEvent.FAULT, _handleTestFault);
var token:AsyncToken = remoteObject.test(data);
token.callBackCommand = callBackCommand;
}
private function _handleTestResult( event:ResultEvent ) : void
{
if (event.token.callBackCommand == "FOO")
{
// do something related to "FOO"
}
else
{
// do something else with the result event
}
}
private function _handleTestFault( event:FaultEvent ) : void
{
//event.token.callBackCommand should be populated here too
}
If you want to access the properties used during the remote call (parameters to the call and/or AsycToken), you can make use of closures. Just define the result event handler inside the calling method as a closure. It can then access any variable in the calling function.
public function testSerivceCall(data:Object, callBackCommand:String):void
{
var _handleTestResult:Function = function( event:ResultEvent ) : void
{
// token is visible here now
if (callBackCommand == "FOO")
{
// do something related to "FOO"
}
else
{
// do something else with the result event
}
}
var remoteObject:RemoteObject;
remoteObject = new RemoteObject();
remoteObject.destination = "zend";
remoteObject.source = "MyService";
remoteObject.endpoint = "http://example.com/service";
remoteObject.test.addEventListener(ResultEvent.RESULT, _handleTestResult);
remoteObject.test.addEventListener(FaultEvent.FAULT, _handleTestFault);
var token = remoteObject.test(data);
}
If I'm reading your question correctly, you're trying to figure out how to access the actual data returned by the ResultEvent ?
If so, assuming you've made the call correctly and you've gotten data back in a format you're expecting:
private function _handleTestResult( event:ResultEvent ) : void
{
// you get the result from the result property on the event object
// edit: assuming the class Person exists with a property called name
// which has the value "John"
var person : Person = event.result as Person;
if (person.name == "John")
{
Alert.show("John: " + person.name);
}
else
{
Alert.show("Not John: " + person.name);
}
}
private function _handleTestFault( event:FaultEvent ) : void
{
// Maybe you know the type of the returned fault
var expectedFault : Object = event.fault as MyPredefinedType
if (expectedFault.myPredefinedTypesPredefinedMethod() == "BAR")
{
// something here
}
}
The ResultEvent has a property called result which will hold an instance of the object returned by the result (it might be the output of an XML file if using a web service, or a serialized object if using AMF, for example). This is what you want to access. Similarly, FaultEvent has a fault property that returns the fault information.
Edit: Changed code in _handleTestResult() in response to Gordon Potter's comment.