I couldn't find any example code or tutorial floating around that creates abstracts with macros.
//Class code ReflectionClassInfo.hx
#:build(ReflectionClassInfoMacro.build())
abstract ReflectionClassInfo({}) from ({}) to ({}) {}
//Driver code
var r=new ReflectionClassInfo();
//Immeditately makes the compiler complain about there is no constructor
How can I fix the compiler error?
One thing that's important to realize is that there isn't really any difference between build macros for classes and abstracts. In both cases, they build fields, meaning that they have to return an array of haxe.macro.Expr.Field. So any documentation or code example that applies to one also applies to the other.
The easiest / most readable way to fix the compiler error in your example is by using class reification, so that the constructor can be declared with regular Haxe syntax:
import haxe.macro.Context;
import haxe.macro.Expr.Field;
class Macro {
public static function build():Array<Field> {
var fields = Context.getBuildFields();
fields = fields.concat((macro class {
public function new() {
this = {};
}
}).fields);
return fields;
}
}
class Main {
static function main() {
new Abstract(); // compiles
}
}
#:build(Macro.build())
abstract Abstract({}) from {} to {} {}
Related
For example, I expect <T extends type> can works like this:
Class Parent {
String data;
Parent({ this.data });
}
Class Child extends Parent {
Child({ this.data }) : Parent(data: data);
void showData() { print(data); }
}
T wrapper<T extends Parent>(String value) {
var result = T(data: value);
return result;
}
void main() {
var trial = wrapper<Child>("Hello world");
trial.showMessage(); // print "Hello world"
}
But turns out it gives me error at var result = T(data: value);, saying that T is not a function. When I specified , I expect that T can be operated like Parent class, and if I supplied its descendant like Child, the operation done will be Child instead. But the constructor will work either way because T extends Parent. Is such thing possible?
Constructors are not inherited. You know that already, because you wrote one yourself in your child class that does nothing but call the base class with the same parameters.
One could as well write a different constructor. So "X extends Y" says a lot about X, but it does not say anything about how the constructor of X looks (or whether it even has a constructor accessible in that scope). So a constructor call is not in the properties available to you when you specify your generic to "extend Y", because Y can do exactly nothing to make sure all it's derivates follow a specific construction method.
Different languages deal with the problem of "but how do I construct a new instance of my generic type" in different ways, but the underlying concept is common to almost all concepts of generics where the generic code is compiled before knowing the specific types of all T's handled. A constructor is not inherited in most OOP languages, therefor it is not guaranteed to be there for any "X extends Y" even if Y has it.
It might be easy to overlook when you have all your code in one compilation unit. The compiler should be able to figure it out, right? But your code might not be in a single compilation unit:
Codebase one:
Class Parent {
String data;
Parent({ this.data });
}
T wrapper<T extends Parent>(String value) {
var result = T(data: value);
return result;
}
At this point, the compiler has no idea what "Child" might look like. It cannot possibly determine that the child class that will be used in the Future has a constructor like that.
Codebase 2:
Class Child extends Parent {
Child({ this.data }) : Parent(data: data);
void showData() { print(data); }
}
void main() {
var trial = wrapper<Child>("Hello world");
trial.showMessage(); // print "Hello world"
}
Now, at this point, a compiler could figure out that the program it's given would actually work. Some concepts of generics do that, where generics cannot be compiled into independent libraries, they always come as source code, because only the final compiler producing the executable can determine whether it would work with a specific class. Flutter does not do this. Flutter needs the generic itself be valid for the constraints given.
All newer language's versions of generics have followed the path of knowing the constraints beforehand and only allowing code operating inside those constraints. And I think it's good because while it has it's shortcomings, it leaves less room for errors or cryptic error messages.
I am a newb to Swift, I am looking to create some nested namespaces, like so:
import Foundation
public class Foo {
class Moo {
class Bar{}
}
}
and then I can do:
var f = Foo.Moo.Bar()
do we not need to use the static keyword here? I don't understand why I don't need to do it like so:
import Foundation
public class Foo {
static class Moo {
static class Bar{}
}
}
var f = Foo.Moo.Bar()
can anyone explain why?
Foo.Moo.Bar is just the name of the class. You're not accessing a particular instance of Foo or Moo when you do this:
var f = Foo.Moo.Bar()
You're just creating an instance of the Foo.Moo.Bar class.
can anyone explain why?
Can you explain why not? What would a static class even mean? How can a class be static? Maybe you come from a language where that keyword means something special in this context?
In any case, in Swift it wouldn't mean anything. The word static has just one very simple meaning in Swift: A type member, i.e. a property (var or let) or method (func) is either an instance member or a type member; to distinguish the latter case, we say static (or class). This is neither of those. It is, as you rightly say, merely a namespaced type.
For sure this kind of stuff could be implemented in some other way but I'm still curious why TS doesn't warn. Same structures could have different data.
class ComponentContainer<TComponent> extends Array<TComponent> {}
class Context<TComponent> extends Array<ComponentContainer<TComponent>> {}
///////////////////////////////////////////////////////
class Component<T> { constructor(public value: T) {} }
class Movable extends Component<boolean> {
constructor(value: boolean = true) { super(value) }
}
class Static extends Component<boolean> {
constructor(value: boolean = true) { super(value) }
}
///////////////////////////////////////////////////////
const ctx: Context<Movable> = new Context()
ctx[0].push(new Static()) // <-- No error
EDIT
Could be a beginning of an alternative here :https://basarat.gitbooks.io/typescript/docs/tips/nominalTyping.html
Types don't presume that value should be an instance of specified class. Movable class is used as interface here:
const ctx: Context<Movable> = new Context()
If a value should be an instance of Movable class, this check is usually performed at run time with if (value instanceof Movable). It will be tricky to implement in Array subclass, it may be better for Context to implement its own API that allows to validate values instead of extending Array.
Same structures could have different data
But they don't have different data. Static conforms to Movable interface, so it doesn't cause type error. Similarly, this will work, because the object conforms to this interface:
ctx[0].push({ value: true })
And will cause TypeScript type error if it doesn't:
ctx[0].push({ value: 1 })
It's possible to additionally enforce the value to be an instance of Movable through type checks with merged interface:
interface Movable {
constructor: typeof Movable
}
class Movable extends Component<boolean> {
constructor(value: boolean = true) { super(value) }
}
It still will be possible to cheat typing system with:
ctx[0].push({ constructor: Movable, value: 1 });
After playing a bit with your code in TypeScript Playground, it seems like TypeScript is smart enough to detect that Movable and Static are basically the same type besides their name. They are both a Component<boolean> and have no properties, so it allows you to push a new Static instance into a Movable array. Only when I added a property to Movable which didn't exist in Static then TypeScript compiler showed an error, for example:
I guess that's just how TypeScript works. It doesn't necessarily prohibit you from using different types on a generic object unless the type that's given is missing properties which exist on the expected type. That's why the following also works:
Sorry for the weird title, I don't quite know how to describe what I'm trying to do in one sentence.
I have to define a bunch of classes that are all going to extend from this one class and also implement this other class.
class SoulCoughing extends Super implements BonBon { /.../ }
class MoveAside extends Super implements BonBon { /.../ }
class LetTheManGoThru extends Super implements BonBon { /.../ }
I have written a sort of wrapper function that I use as a decorator for these classes.
const Eminem = function(klass: Constructable<????>) {
const instance = new klass();
// Do stuff
}
Constructable is a little interface I'm using because otherwise TypeScript would throw an error about not having a constructor.
interface Constructable<T> {
new(): T;
}
Now here is my problem, I don't know what type to assign to parameter klass in my wrapper function? I have tried doing this:
... function(klass: Contrusctable<Super & BonBon>)
and this:
... function(klass: Contrusctable<Super | BonBon>)
I also tried modifying my constructable interface like this:
interface Constructable<T, U> {
new(): T & U;
}
... function(klass: Contrusctable<Super, BonBon>)
but I keep getting an Argument of type 'typeof SoulCoughing' is not assignable to parameter of type 'Constructable<everythingIveTriedSoFar>' error.
So my question is, what type definition should I use with the parameter klass? I know I can just use any but I'd really like to make sure that the class being passed has extended Super and implemented BonBon.
I'm going to guess that the classes SoulCoughing etc. don't actually have no-arg constructors, and therefore cannot act as Constructable<{}> at all; the most likely culprit is that Super's constructor has a mandatory argument, which would make all subclasses fail to match new() by default. Note that this also implies that your implementation of Eminem probably wants to call new klass(...) with some arguments also.
The right way to fix it is to declare Constructable<T> to be a constructor with the right argument types. Let's say Super looks like this:
class Super {
constructor(elevator: number, mezzanine: string) {
//...
}
}
Then you could define Constructable to match:
interface Constructable<T extends Super & BonBon = Super & BonBon> {
new(chump: number, change: string): T; // same args as Super
}
and Eminem like:
const Eminem = function(klass: Constructable) {
const instance = new klass(2, "rise");
// Do stuff
}
and finally:
Eminem(SoulCoughing); // no error
I only kept Constructable generic in case you wanted TypeScript to preserve the type of the particular subclass, like so:
const SlimShady = function <T extends Super & BonBon>(klass: Constructable<T>): T {
return new klass(2, "fat");
}
// returns same type as passed-in constructor
const cutLean: MoveAside = SlimShady(MoveAside);
Okay, hope that helps; good luck!
I have this Haxe class that is growing quite large. It consists mostly of static methods & properties. (It's a module set to compile as JS target).
I would like to separate some of the complex static functions into another class.
Is there any way to mark it with a metatag / indicate the other class is an "extension" to the original class?
Something like #:native("OriginalClass") class OtherClass {...}
The goal is to avoid having to write the full variable access (ex: OriginalClass.LOG_QUEUE vs. LOG_QUEUE) or clutter the imports with each OriginalClass's static methods / properties used at the top of the OtherClass. Basically, something to make it aware that it "is" using the same members as the OriginalClass (whenever an 'undefined' one is found, at compile-time).
Example:
If OriginalClass has static var LOG_QUEUE:Array<String>; then OtherClass would be aware that any usage of LOG_QUEUE compiles to this JS code OriginalClass.LOG_QUEUE
Alright, got a solution after discussing with Dima Granetchi from the Haxe experts group on Slack.
Now, although this will still generate the OtherClass that makes use of the OriginalClass's static members, you can cut down on the quantity of import statements for most (if not all) of the module/class's static members by using the wildcard * symbol, like in this example:
// OriginalClass.hx
package somePackage;
class OriginalClass {
public static var LOG_QUEUE:Array<String>;
public static function main() {
LOG_QUEUE = [];
OtherClass.doSomething();
}
public static function doSomethingOriginal() {
LOG_QUEUE.push("World!");
}
}
// OtherClass.hx
import somePackage.OriginalClass.*; // <-- Demonstrating the WILDCARD (*) symbol
class OtherClass {
public static function doSomething() {
LOG_QUEUE.push("Hello"); //Resolved to OriginalClass.LOG_QUEUE
doSomethingOriginal(); //Resolved to OriginalClass.doSomethingOriginal()
}
}
Although this is a minimal example, it becomes more useful when you have a few different dozen static members used in your OtherClass.
Note
TypeDefs defined in the OriginalClass used inside the OtherClass doesn't seem to get recognized/resolved (may be due to missing public accessor, but I was unable to set it on my typedefs). You can always import those specific TypeDefs with individual import statements, like so:
//Somewhere at the top of OtherClass.hx...
import somePackage.OriginalClass.MyTypeDef;