Proper Uses for Abstracts
Asked Answered
C

1

14

Just yesterday, I decided to begin learning the Haxe programming language after having used Actionscript 3 for the past few years. Today I have been exploring abstract types, and I have come to realize that they seem quite different from abstract classes in Java. I am beginning to grasp some of what they do, but I am unsure of what abstracts are used for. What constitutes the proper use of abstracts in Haxe, and when ought I to favor them over classes?

For instance, below is an incomplete definition for a complex number type using an abstract type. Ought I to prefer this or just an ordinary class?

abstract Complex({real:Float, imag:Float}) {
    public function new(real:Float, imag:Float) {
        this = { real: real, imag: imag };
    }

    public function real():Float {  return this.real; }
    public function imag():Float {  return this.imag; }

    @:op(A + B)
    public static function add(lhs:Complex, rhs:Complex):Complex {
        return new Complex(lhs.real() + rhs.real(), lhs.imag() + rhs.imag());
    }

    public function toString():String {
        return real() + " + " + imag() + "i";
    }
}
Chemiluminescence answered 24/12, 2014 at 23:7 Comment(0)
C
22

Indeed abstracts are not at all like abstract classes in Java. Abstract types in Haxe are powerful and interesting. Their main characteristic is that they are types that exist only at compile-time. At runtime they are entirely replaced by the wrapped type. Methods are transformed into static functions. In the case you described all of your instances will be replaced by anonymous objects with the two fields real and imag. Is that a good use case? Probably yes since a Complex type is not meant to be extended and you probably want to define some operator overloading (as you did for the addition).

To keep it even more light-weight you could use an Array<Float> as the wrapped type where the first element is the real part and the second the imaginary one.

So what is good about abstract types?

  • they add semantic to types (particularly primitive types). For example you could define an abstract RGB(Int) {} to always output very efficient color encoding with the benefit of methods and properties. Or you could have an abstract Path(String) {} to conveniently deal with path concatenation, relative paths and the like.
  • you can define operator overloading. In the case above you could do something like white + black and get something meaningful out of it.
  • similarly to operator overloading, abstracts can define implicit casts from and to other types. In the case of the RGB above you could easily define a method fromString() to parse an hex string into an Int representing a color. With the implicit cast you could do: var color : RGB = "#669900";. thx.color defines a lot of abstracts for color handling.
  • they are ideal to wrap the very powerful Enums in Haxe. With an abstract you can add methods and properties to enumerations (that natively do not support any of that).
  • they are ideal to wrap optimized code. Abstract methods can be inlined and the wrapped type ensures that you are not adding any additional layer of indirection when executing your code.

What is not so good? Or better, what we should know about abstracts?

  • since they are just a compile-time artifact, you cannot use runtime checks (eg: no Std.is(value, MyAbstract)).
  • abstracts are not classes, so no inheritance.
Canicular answered 25/12, 2014 at 4:55 Comment(1)
That first bullet point made it really click for me! Now I see why they are called "abstracts" (:Chemiluminescence

© 2022 - 2024 — McMap. All rights reserved.