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;
}
}
Related
I have a simple NumericStepper which looks like this:
<mx:NumericStepper id="nsPort"
minimum="0"
maximum="65535"
stepSize="1"
value="{PORT_DEFAULT}"/>
I wrote a Validator for it (which doesn't work as I expect):
<mx:NumberValidator
required="true"
source="{nsPort}"
property="value"/>
However, every time I don't enter anything into NumericStepper it passes zero, not NULL or something else, which is...well...a valid value. I suspect that this is done on purpose. So, how can I make blank value at NumbericStepper to be invalid?
Please note that I'm restricted to a ver.3 of Action Script.
Example code base from https://flexscript.wordpress.com/2008/09/22/flex-creating-custom-validators/ with a little modification:
package flexScript
{
import mx.validators.ValidationResult;
import mx.validators.Validator;
//Class should extend mx.validators.Validator
public class NumericStepperValidator extends Validator {
public function NumericStepperValidator() {
// Call base class constructor.
super();
}
// Class should override the doValidation() method.
//doValidation method should accept an Object type parameter
override protected function doValidation(value:Object):Array {
// create an array to return.
var ValidatorResults:Array = new Array();
// Call base class doValidation().
ValidatorResults = super.doValidation(value);
// Return if there are errors.
if (ValidatorResults.length > 0)
return ValidatorResults;
if (String(value).length == 0)
return ValidatorResults;
if ( value == 0)//as your required.
return ValidatorResults;
var RegPattern:RegExp = /\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/; //Change to your customize regExpression
var a:Array = RegPattern.exec(String(value));
if (a == null)
{
ValidatorResults.push(new ValidationResult(true, null, "NumbericStepper Error","You must enter an Number"));
return ValidatorResults;
}
return ValidatorResults;
}
}
}
<flexScript:NumbericStepperValidator source="{{nsPort}}" property="number"/>
I have an enum property in the view-model and I want to make a label visible if the property value is of a specific value
States state;
public States State
{
get { return this.state; }
set
{
if (this.state != value)
{
this.state = value;
this.RaisePropertyChanged("State");
}
}
}
public enum States
{
Stopped,
Runnning
}
here's what I tried in the UI:
local:MvxBind="Visibility If(State==1, 'Visible', 'Gone')"
but it doesn't work. No error is displayed in the log.
I can make a converter but I was wondering if it's possible to do it without it.
If I modify the State property to use int instead of Enum, it works fine.
I guess it has to do with how currently Enum type is handled.
In the Tibet binding addons, statements like:
If(State==1, 'Visible', 'Gone'
are not compiled because of iOS JIT limitations - instead they are evaluated.
Sadly the evaluation of operators like == turned out to be quite difficult to do without using dynamic code...
Because of this, what the Tibet == operator tries to do is to reduce the right and left sides down to one of three types: long, double or object - it does this using:
private Type GetLookupTypeFor(object value)
{
if (value == null)
return null;
if (value is long)
return typeof(long);
if (value is double)
return typeof (double);
return typeof (object);
}
from https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Binding/Combiners/MvxPairwiseValueCombiner.cs#L36
with these types determined it then calls one of these methods, each of which uses the == operator of the left-hand type or .Equals if both types are object:
protected override bool CombineDoubleAndDouble(double input1, double input2, out object value)
{
value = input1 == input2;
return true;
}
protected override bool CombineDoubleAndLong(double input1, long input2, out object value)
{
value = input1 == input2;
return true;
}
// ... etc
from https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Binding/Combiners/VeryExperimental/MvxEqualToValueCombiner.cs
For the enum case of State==1, I think the left-hand will be identified as an object and the right-hand as a long - so these will never be equal because of:
protected override bool CombineObjectAndLong(object input1, long input2, out object value)
{
value = false;
return true;
}
Working with the current code, I think the only way to get the test to work is to get both left and right-side to long or to string.
You could do that using a value converter on the enum - e.g. a ToStringValueConverter or a ToLongValueConverter which you could then use like:
If(ToLong(State)==1, 'Visible', 'Gone'
or:
If(ToString(State)=='Running', 'Visible', 'Gone'
Obviously, looking forwards, this area could also be improved inside Mvx - in whatever lies beyond Swiss and Tibet
Just wondering, if i could provide default argument for function in ActionScript 3 which is never can be passed to the function by user. This is the case:
public function getAttr (obj:Object, key:String, def:* = DEFAULT_VALUE):* {
if (!obj.hasOwnProperty(key)) {
if (def === DEFAULT_VALUE) {
throw ReferenceError('Attribute not found: ' + key);
} else {
return def;
}
} else {
return obj[key];
}
}
I cannot use as DEFAULT_VALUE any of null, undefined, Number, Boolean or String here, cause, logically, user could use any of this values. I need something really unique here. In python, for example, i can do this:
_DEFAULT_VALUE = object()
def get_attr(obj, key, d=_DEFAULT_VALUE):
if not hasattr(obj, key):
if d is DEFAULT_VALUE:
raise KeyError('Attribute not found: {}'.format(key))
else:
return d
else:
return obj[d]
But in ActionScript 3 such approach produces an error:
Error code: 1047: Parameter initializer unknown or is not a compile-time constant.
Maybe some hack here?
Your problem here is that def value have to be a compile time constant so there is no way to store a default value into a var and pass it as a default value.
But what you really want here is to know if the user have passed an extra parameter into the def field of the function, so you can check the arguments array length and see if there is 2 or 3 parameters passed.
public function getAttr (obj:Object, key:String, def:* = null):* {
if (!obj.hasOwnProperty(key)) {
if (arguments.length==2) { // no default value passed to the function
throw new ReferenceError('Attribute not found: ' + key);
} else {
return def;
}
} else {
return obj[key];
}
}
Transfer null, and check for null. After all, null is a default value that's really nothing, if you require a parameter to be supplied and be valid, use null as default value so that if something will be supplied, it will not be null, thus valid.
Vesper's answer seems correct for your situation. But, I've used similar method for singletons though:
class Singleton
{
private var k_CONS_BLOCKER:Object = {};
public Singleton(cons_blocker:Object)
{
if(cons_blocker!=k_CONS_BLOCKER)
{
throw new Error("Use static getter to get the object");
}
}
public function getObject():Singleton
{
return obj;//you can of course create it now or do some other logic...
}
}
I know the answer isn't directly related to the question, but I think the idea solves the problem in general.
HTH.
A predicate (an object that is a boolean-valued function which tests its input for a condition) is generally assumed to be stateless.
What's the most appropriate name for an object which has a testing function with state?
e.g. in Java, the CountTrigger class below returns true only on the Nth time it is tested against a value that matches a desired value, and false otherwise.
interface QuasiPredicate<T> // what should this be renamed to?
{
public boolean test(T value);
}
class CountTrigger<T> implements QuasiPredicate<T>
{
// for simplicity, ignore synchronization + null-value issues
private int remainingTriggers = 0;
final private T testValue;
public CountTrigger(T testValue, int count)
{
this.remainingTriggers = count;
this.testValue = testValue;
}
#Override public boolean test(T value)
{
if (!this.testValue.equals(value))
return false;
if (this.remainingTriggers == 0)
return false;
if (--this.remainingTriggers == 0)
return true;
}
}
Considering it's an interface and interfaces are implemented and not extended then I don't see the problem in your object implementing a predicate.
If you're going to put public CountTrigger(T testValue, int count) in the interface as well then maybe you need a different name. Perhaps IFiniteRule or another suitable synonym. Maybe ask at https://english.stackexchange.com/ ;-)
I have several data manipulation widgets that all implement a custom IPropertyEditor interface. I would like to include a GetValue getter, but the return types could be String, Boolean or int, off the top of my head. In AS3, all of that inherits from Object, so I could have GetValue return an object, but I don't feel great about that approach. At the risk of asking a subjective question, does anyone have any recommendations on how to approach this?
In ActionScript I'm pretty sure you can set a variable return type by defining a function in the following way:
public function getValue():* {
return "Any of these would be fine.";
return true;
return 1;
return new Sprite();
}
Hope that helps.
In practice, there is an actual getter/setter model in ActionScript. For your case, you could use it like this:
private var _value:*;
public function get value() : * {
return _value;
}
public function set value(val:*) : void {
if (typeof val == "int" || typeof val == "boolean" || typeof val == "string") {
_value = val;
}
}
This limits the user to setting (per your requirements) the value of this "value" property to data types int, Boolean, or String.