Why do setters need to return a value in Haxe? - haxe

I was recently tripped up by the fact that the expected type of a setter that sets an Int is Int -> Int.
Why does a setter return a value? What significance does this value have?

Small addition to other answers over here, it allows you to do this:
x = y = z = 5;

The value that the setter returns is the value of the assignment expression.
For instance, if you were to do something like this:
public var x(setX, getX): Int
public function setX(val: Int): Int {
return 5;
}
static function main() {
neko.Lib.print(x = 2); // Prints 5
}
Haxe would print out 5. The setter returns the value of the set expression. Of course, it's a nonsensical value in this case.

In the real world, it permits a way of implementing copy by value on assignment, eg:
public var pos(set_pos, default):Point;
function set_pos(newPos:Point) {
pos.x = newPos.x
pos.y = newPos.y;
return pos;
}
And also of having a sensible return when implementing setters that can fail, eg:
public var positiveX(default, set_positiveX):Int;
function set_positiveX(newX:Int) {
if (newX >= 0) x = newX;
return x;
}
trace(x = 10); // 10
trace(x = -4); // 10
trace(x); // 10
Whereas you would otherwise get something like this:
trace(x = 10); // 10
trace(x = -4); // -4
trace(x); // 10
Which is what happens in AS3 even if the setter does not modify the value. If you could not do this, clearly the first, where x = -4 returns 10 is better :)

Related

Create new this on method call

This may be a stupid question, but is it possible to create a new this on a method call of a class?
E.g:
const foo = new Foo();
console.log(foo.a(1).b(2));
// for example, outputs 3 (1+2)
// the a method will create a new namespace and attach 1 to it, and b will use that new namespace
console.log(foo.b(2));
// this will result in an error, as there is no new namespace from the a method anymore, so b cannot add to anything?
Maybe this is too hard to understand, sorry.
class Foo {
a(number) {
this.a = number;
return this;
}
b(number) {
return this.a + number;
}
}
This would be the code where it uses the same this variable - this doesn't fit what I wanted but is what I currently have.
// pseudo
class Foo {
a(number) {
const uniqueVariable = number
return uniqueVariable
// it'll somehow pass the number from this method to the next method
}
// where it can be used with the second method's input
b(uniqueVariable, number) {
return uniqueVariable + number
}
}
foo.a(1).b(2) = 3
This example would obviously cause an error because the return value of a() a number, not something to use a method on again.
Please let me know if I need to explain further -- I'm having some struggle explaining it properly.
If the intention is that foo.a(1).b(2) changes foo, or if you don't mind changing foo, the other answers here work.
But if you only want foo.a(1).b(2) to return 3 without modifying foo, then you need to return a new Foo.
Now, if you really hell bent on having console.log() print 3 rather than something like Foo { value: 3 }, you can also customize inspect() (given that the question is tagged with node.js).
All together:
const util = require('util');
class Foo {
constructor(value) {
this.value = value || 0;
}
add(value) {
return new Foo(this.value + value);
}
a(value) {
return this.add(value);
}
b(value) {
return this.add(value);
}
[util.inspect.custom]() {
return this.value;
}
}
const foo = new Foo();
console.log(foo);
console.log(foo.a(2).b(1));
console.log(foo);
Output:
0
3
0
On my solution, I decided to create two variables to hold the values of each method. (https://jsbin.com/wozuyefebu/edit?js,console)
The a() method will return a number if the isSingle parameter is set to true. If not, it will return the this object, allowing you to chain the b() method. This is might be a hack but I believe it solves your problem.
I write about Javascript and web development on my blog :) https://changani.me/blog
class Foo {
constructor() {
this.aValue = 0;
this.bValue = 0;
}
/**
* #param {Number} value
* #param {Boolean} isSingle
* #returns {Object/Number}
*/
a(value = 0, isSingle = false) {
this.aValue = value;
return isSingle ? this.aValue : this;
}
/**
* #param {Number} value
* #returns {Number}
*/
b(value = 0) {
this.bValue = this.aValue + value;
return this.bValue;
}
}
const x = new Foo();
console.log("Should return 3: ", x.a(2).b(1));
console.log("Should return an 2: ", x.a(2, true));
console.log("Should return an instance of the object: ", x.a(2));
console.log("Should return 1: ", x.b(1));
console.log("Should return 0: ", x.a().b());
(https://jsbin.com/wozuyefebu/edit?js,console)
If you want to be able to invoke methods on return value of methods, then, you should return this from those methods. However, you will need an additional method, say value() to actuall get the result of sum.
A possible way is show below.
class Foo {
_a = 0;
_b = 0;
a(number) {
this._a = number;
return this;
}
b(number) {
this._b = number;
return this;
}
value() {
return this._a + this._b;
}
}
const foo = new Foo();
console.log(foo.a(1).b(2).value());
console.log(foo.b(5).value());

How to multiply strings in Haxe

I'm trying to multiply some string a by some integer b such that a * b = a + a + a... (b times). I've tried doing it the same way I would in python:
class Test {
static function main() {
var a = "Text";
var b = 4;
trace(a * b); //Assumed Output: TextTextTextText
}
}
But this raises:
Build failure Test.hx:6: characters 14-15 : String should be Int
There doesn't seem to be any information in the Haxe Programming Cookbook or the API Documentation about multiplying strings, so I'm wondering if I've mistyped something or if I should use:
class Test {
static function main() {
var a = "Text";
var b = 4;
var c = "";
for (i in 0...b) {
c = c + a;
}
trace(c); // Outputs "TextTextTextText"
}
}
Not very short, but array comprehension might help in some situations :
class Test {
static function main() {
var a = "Text";
var b = 4;
trace( [for (i in 0...b) a].join("") );
//Output: TextTextTextText
}
}
See on try.haxe.org.
The numeric multiplication operator * requires numeric types, like integer. You have a string. If you want to multiply a string, you have to do it manually by appending a target string within the loop.
The + operator is not the numeric plus in your example, but a way to combine strings.
You can achieve what you want by operator overloading:
abstract MyAbstract(String) {
public inline function new(s:String) {
this = s;
}
#:op(A * B)
public function repeat(rhs:Int):MyAbstract {
var s:StringBuf = new StringBuf();
for (i in 0...rhs)
s.add(this);
return new MyAbstract(s.toString());
}
}
class Main {
static public function main() {
var a = new MyAbstract("foo");
trace(a * 3); // foofoofoo
}
}
To build on tokiop's answer, you could also define a times function, and then use it as a static extension.
using Test.Extensions;
class Test {
static function main() {
trace ("Text".times(4));
}
}
class Extensions {
public static function times (str:String, n:Int) {
return [for (i in 0...n) str].join("");
}
}
try.haxe.org demo here
To build on bsinky answer, you can also define a times function as static extension, but avoid the array:
using Test.Extensions;
class Test {
static function main() {
trace ("Text".times(4));
}
}
class Extensions {
public static function times (str:String, n:Int) {
var v = new StringBuf();
for (i in 0...n) v.add(str);
return v.toString();
}
}
Demo: https://try.haxe.org/#e5937
StringBuf may be optimized for different targets. For example, on JavaScript target it is compiled as if you were just using strings https://api.haxe.org/StringBuf.html
The fastest method (at least on the JavaScript target from https://try.haxe.org/#195A8) seems to be using StringTools._pad.
public static inline function stringProduct ( s : String, n : Int ) {
if ( n < 0 ) {
throw ( 1 );
}
return StringTools.lpad ( "", s, s.length * n );
}
StringTools.lpad and StringTools.rpad can't seem to decide which is more efficient. It looks like rpad might be better for larger strings and lpad might be better for smaller strings, but they switch around a bit with each rerun. haxe.format.JsonPrinter uses lpad for concatenation, but I'm not sure which to recommend.

How to call multi argument function using ArrayList?

Im trying to call this method from SDK
public ThumborUrlBuilder crop(int top, int left, int bottom, int right) {
if (top < 0) {
throw new IllegalArgumentException("Top must be greater or equal to zero.");
}
if (left < 0) {
throw new IllegalArgumentException("Left must be greater or equal to zero.");
}
if (bottom < 1 || bottom <= top) {
throw new IllegalArgumentException("Bottom must be greater than zero and top.");
}
if (right < 1 || right <= left) {
throw new IllegalArgumentException("Right must be greater than zero and left.");
}
hasCrop = true;
cropTop = top;
cropLeft = left;
cropBottom = bottom;
cropRight = right;
return this;
}
How I can call the method if the parameters are from an Array or Map like this? Is that possible?
ArrayList arrayList = [299, 296, 301, 297]
crop(arraylist)
Java:
No you cant.
you will get this error:
Compilation Errors Detected
...
method crop in class Test cannot be applied to given types;
required: int,int,int,int
found: java.util.ArrayList<java.lang.Integer>
reason: actual and formal argument lists differ in length
Groovy:
Yes you can.
Check the sample code on groovyConsole.
def hello(int a, int b){
println "$a and $b"
}
hello(1, 2)
def param = [1,2]
hello(param)
public ThumborUrlBuilder crop(ArrayList params) {
if (params.size() != 4 ){
throw new IllegalArgumentException(...);
}
int top = params.get(0);
int left = params.get(1);
int bottom = params.get(2);
int right = params.get(3);
...
}
This is not directly possible in Java, because the function crop requires 4 parameters.
Passing the given ArrayList into the crop function would result in an error.
You could write your own function to handle the ArrayList for you like this:
public ThumboUrlBuilder special_crop(ArrayList arraylist){
crop(arraylist.get(0),arraylist.get(1),arraylist.get(2),arraylist.get(3));
}

haxe "should be int" error

Haxe seems to assume that certain things must be Int. In the following function,
class Main {
static function main() {
function mult_s<T,A>(s:T,x:A):A { return cast s*x; }
var bb = mult_s(1.1,2.2);
}
}
I got (with Haxe 3.01):
Main.hx:xx: characters 48-49 : mult_s.T should be Int
Main.hx:xx: characters 50-51 : mult_s.A should be Int
Can anyone please explain why T and A should be Int instead of Float?
A more puzzling example is this:
class Main {
public static function min<T:(Int,Float)>(t:T, t2:T):T { return t < t2 ? t : t2; }
static function main() {
var a = min(1.1,2.2); //compile error
var b = min(1,2); //ok
}
}
I can't see why t<t2 implies that either t or t2 is Int. But Haxe seems prefer Int: min is fine if called with Int's but fails if called with Float's. Is this reasonable?
Thanks,
min<T:(Int,Float)> means T should be both Int and Float. See the constraints section of Haxe Manual.
Given Int can be converted to Float implicitly, you can safely remove the constraint of Int. i.e. the following will works:
http://try.haxe.org/#420bC
class Test {
public static function min<T:Float>(t:T, t2:T):T { return t < t2 ? t : t2; }
static function main() {
var a = min(1.1,2.2); //ok
$type(a); //Float
trace(a); //1.1
var b = min(1,2); //ok
$type(b); //Int
trace(b); //1
}
}

Do we have to return value at setter?

In haxe documentation of properties, there is the example:
class C {
public var x(get,set) : Int;
function get_x(){ return 123; }
function set_x(value){
doSomethingWith(value);
return 123;
}
}
But why do we have to return a value in setter of x above? is there a good reason?
The reason is, in Haxe, the assignment expression does return a value, eg.
var a;
trace(a = 3.14);//3.14
It is natural since we can chain assignments together:
var test = a = 3.14; //test will be 3.14
For example there is a weird class,
class Weird {
public function new():Void {}
public var x(get, set):Int;
function get_x() return x;
function set_x(v:Int):Int {
x = v;
return 123;
}
}
var weird = new Weird();
trace(weird.x = 456); //123
trace(weird.x); //456
var test = weird.x = 456; //test will be 123
But of course, usually we simply return the input of the setter, because it is more logical:
function set_x(v:Int):Int {
return x = v;
}
Sometimes it's just nice to have a setter function return the previous value, so you can code like this:
oldval=set(newval);
do_something();
set(oldval);
to temporarily set a new value, then restore the old one after you've finished.

Resources