haxe get type info for fields of a function type parameter - haxe

Reflect can list an object's fields at runtime, but doesn't have type info. rtti has type info, but doesn't seem to work for type parameters.
In this example, I want MyLibrary to be able to figure out that T's val field is an Int.
import haxe.Constraints;
class Main {
static function main() {
var obj :IntStruct = MyLibrary.foo();
}
}
#:rtti
class IntStruct {
public var val :Int;
public function new() { }
}
class MyLibrary {
#:generic
public static function foo<T:Constructible<()->Void>>() :T {
var something = new T();
trace(Reflect.fields(something)); // [val]
trace(haxe.rtti.Rtti.getRtti(T)); // fails here
return something;
}
}
this gives this compile error:
Main.hx:20: characters 38-39 : Only #:const type parameters on #:generic classes can be used as value
if I add #:const to the type parameter declaration, I get this instead
Main.hx:20: characters 38-39 : foo.T should be Class<Unknown<0>>
Main.hx:20: characters 38-39 : ... For function argument 'c'
Since foo is generic, the compiler will write a separate function for each T, so it should be no problem for it to tell me about T inside foo. How can it be done?

Related

custom cast function and abstracts

This code outputs Null<_Test.Bar_Impl_>. I wanted it to output Foo but I see why it does not work that way. But may be I can somehow overcome this limitation.
My primary goal is to create function that will work like cast, but return null instead of throwing exception. And it should work with abstracts.
class Foo {
}
abstract Bar(Foo) {
}
class MyCast {
inline static public function doCast<T>(value: Any, type: Class<T>): Null<T> {
return Std.is(value, type) ? cast value : null;
}
}
class Test {
static function main() {
$type(MyCast.doCast(null, Bar));
}
}
Actually that cannot work at all like that, since Std.is(value, AbstractType) will always fail because the abstract does not exist any more at runtime.
See https://try.haxe.org/#1Afb5, and especially:
Use #:forward to access foo from Bar instances (forward doc)
Use from Foo to safe cast Foo instances into Bar instances (see implicit cast doc) (note that this feature on itself may be exactly what you were trying to achieve: https://try.haxe.org/#cc903)

Assigning default value to T:Float type parameter

I have a class that I wanted dynamic on what type to accept, but still be of type float. I have added an example class below. Simply put, I want a class that can contain either Ints or Floats (or abstracts(Float)), but the type parameter doesn't like being assigned something that should actually fit it.
class Container<T:Float>
{
public function new(aValue:T = 0.0)
{
}
public function example():T
{
return 16.0;
}
In this example, I get two compiler errors. The fist one is the default value of the constructor new(aValue:T = 0.0. A simple fix is to set the value as dynamic, but I like my code neater than this. The second error is in the return value of example(). It won't let me return 16.0, as it is not a T instance.
My question: Is this doable and, if not, should I either use different class definitions for every type?
I think the issue here is that you don't really need the generic type "T".
Here's what I came up with given your constraints. The class "Container" is not generic, and merely contains a Float constructor. This still allows it, however, to accept any value that can be implicitly cast to Float, which includes any abstract as long as they define casting rules.
package ;
class Main
{
public static function main()
{
new Container(); // default
new Container(1); // Int
new Container(2.3); // Float
new Container(new UnifiesWithFloat(4.5)); // Float abstract
}
}
class Container
{
public function new(aValue:Float = 0.8)
{
trace('aValue is $aValue');
}
}
abstract UnifiesWithFloat(Float) from Float to Float
{
inline public function new(value:Float)
{
this = value;
}
}
The only way I could come up for this issue with a cast and with own resolving of the optional parameters.
class Test {
static function main() {
$type(new Container(1));
$type(new Container(1).example());
new Container(1).example();
$type(new Container(1.0));
$type(new Container(1.0).example());
new Container(1.0).example();
}
}
class Container<T:Float> {
public var value:T;
public function new(aValue:T) {
this.value = cast (aValue != null ? aValue : 0);
}
public function example():T {
return cast 16;
}
}
This logs:
Test.hx:3: characters 14-30 : Warning : Container<Int>
Test.hx:4: characters 14-40 : Warning : Int
Test.hx:7: characters 14-32 : Warning : Container<Float>
Test.hx:8: characters 14-42 : Warning : Float

Unification and implicit cast of the type parameter

class Base, and class Ext extends Base.
class B<T> with typed method foo<T>(value:T)
Why B<Base>.foo doest not accept instance of B<Ext> (implicit downcast of the type parameter?) by default?
Here is an example
http://try.haxe.org/#d443f
class Test {
static function main() {
var bExt = new B(new Ext());
var bBase = new B(new Base());
bBase.foo(bExt);
//ofc
//bBase.foo(cast bExt);
}
}
class B<T>
{
public function new(v:T)
{
}
public function foo(v:B<T>)
{
//
}
}
class Base {
public function new(){}
}
class Ext extends Base {
public function new(){
super();
}
}
Is there any way to trigger implicit cast of the type parameter for B.foo?
There are three ways to interpret and answer your question:
1. foo(v:B<T>):
This is your example and it doesn't compile because T isn't allowed to be be variant. It happens because of the very existence of foo and because allowing bBase.foo(bExt), that is, unifying bExt with bBase, will then allow bBaseOfbExt.foo(bBase).
It is the fact that foo exists and that it can potentially modify the type that makes the bExt unification with bBase unsafe; you can see a similar (but maybe clearer) explanation in the manual, using arrays: type system – variance.
2. foo(v:T):
This is closer to what's on the body of your question (but not in the example) and it works fine.
3. foo<A>(v:B<A>):
Finally, if you have a type parameterized method, it also works, but you'd probably face other variance issues elsewhere.

Casting Dynamic to an other class

I would like to know if that's possible to cast a Dynamic to an other class (partially or totally)
For example, this code breaks :
class Test {
public function new() {}
public var id: String;
}
class Main {
public static function main() {
var x:Dynamic = JsonParser.parse("{\"id\":\"sdfkjsdflk\"}");
var t:Test = cast(x, Test);
}
}
with the following message
Class cast error
However, my "Test" class has an "id" field like the dynamic object. (That's an example, my use case is more complexe than that ^^)
So, I don't understand how to get an object from my Dynamic one.
This isn't exactly casting a dynamic to a class instance but may accomplish the same thing:
create an empty instance of the class with Type.createEmptyInstance
set all of the fields from the Dynamic object on the new class instance using Reflect
Example:
import haxe.Json;
class Test {
public function new() {}
public var id: String;
}
class Main {
public static function main() {
var x:Dynamic = Json.parse("{\"id\":\"sdfkjsdflk\"}");
var t:Test = Type.createEmptyInstance(Test);
for (field in Type.getInstanceFields(Test))
if (Reflect.hasField(x, field))
Reflect.setProperty(t, field, Reflect.getProperty(x, field));
trace(t.id);
}
}
You could use typedef
typedef Test = {
public var id: String;
}
class Main {
public static function main() {
var t:Test = JsonParser.parse("{\"id\":\"sdfkjsdflk\"}");
}
}
Json.parse returns anonymous structure(implementation platform dependent), typed as Dynamic. There isn't a single chance to cast it to anything but Dynamic, unless Json.parse returns Int, Float or String, which some parsers permit, but which isn't actually permitted by JSON specification.
That is this way because, the operation of casting doesn't check what fields some object have. Operation of casting only checks if the object is an instance of class you are casting to. Obviously, anonymous structure can't be an instance of any class(inside haxe abstractions at least).
However, the right way to perform the thing you seem to be trying to perform is the way stated by #Ben Morris, in his answer.

Swift class introspection & generics

I am trying to dynamically create a class instance based type using generics, however I am encountering difficulty with class introspection.
Here are the questions:
Is there a Swift-equivalent to Obj-C's self.class?
Is there a way to instantiate a class using the AnyClass result from NSClassFromString?
Is there a way to get AnyClass or otherwise type information strictly from a generic parameter T? (Similar to C#'s typeof(T) syntax)
Well, for one, the Swift equivalent of [NSString class] is .self (see Metatype docs, though they're pretty thin).
In fact, NSString.class doesn't even work! You have to use NSString.self.
let s = NSString.self
var str = s()
str = "asdf"
Similarly, with a swift class I tried...
class MyClass {
}
let MyClassRef = MyClass.self
// ERROR :(
let my_obj = MyClassRef()
Hmm… the error says:
Playground execution failed: error: :16:1: error: constructing an object of class type 'X' with a metatype value requires an '#required' initializer
Y().me()
^
<REPL>:3:7: note: selected implicit initializer with type '()'
class X {
^
It took me a while to figure out what this means… turns out it wants the class to have a #required init()
class X {
func me() {
println("asdf")
}
required init () {
}
}
let Y = X.self
// prints "asdf"
Y().me()
Some of the docs refer to this as .Type, but MyClass.Type gives me an error in the playground.
Here's how to use NSClassFromString. You have to know the superclass of what you're going to end up with. Here are a superclass-subclass pair that know how to describe themselves for println:
#objc(Zilk) class Zilk : NSObject {
override var description : String {return "I am a Zilk"}
}
#objc(Zork) class Zork : Zilk {
override var description : String {return "I am a Zork"}
}
Notice the use of the special #obj syntax to dictate the Objective-C munged name of these classes; that's crucial, because otherwise we don't know the munged string that designates each class.
Now we can use NSClassFromString to make the Zork class or the Zilk class, because we know we can type it as an NSObject and not crash later:
let aClass = NSClassFromString("Zork") as NSObject.Type
let anObject = aClass()
println(anObject) // "I am a Zork"
And it's reversible; println(NSStringFromClass(anObject.dynamicType)) also works.
Modern version:
if let aClass = NSClassFromString("Zork") as? NSObject.Type {
let anObject = aClass.init()
print(anObject) // "I am a Zork"
print(NSStringFromClass(type(of:anObject))) // Zork
}
If I'm reading the documentation right, if you deal with instances and e.g. want to return a new instance of the same Type than the object you have been given and the Type can be constructed with an init() you can do:
let typeOfObject = aGivenObject.dynamicType
var freshInstance = typeOfObject()
I quickly tested it with String:
let someType = "Fooo".dynamicType
let emptyString = someType()
let threeString = someType("Three")
which worked fine.
In swift 3
object.dynamicType
is deprecated.
Instead use:
type(of:object)
Swift implementation of comparing types
protocol Decoratable{}
class A:Decoratable{}
class B:Decoratable{}
let object:AnyObject = A()
object.dynamicType is A.Type//true
object.dynamicType is B.Type//false
object.dynamicType is Decoratable.Type//true
NOTE: Notice that it also works with protocols the object may or may not extend
Finally got something to work. Its a bit lazy but even the NSClassFromString() route did not work for me...
import Foundation
var classMap = Dictionary<String, AnyObject>()
func mapClass(name: String, constructor: AnyObject) -> ()
{
classMap[name] = constructor;
}
class Factory
{
class func create(className: String) -> AnyObject?
{
var something : AnyObject?
var template : FactoryObject? = classMap[className] as? FactoryObject
if (template)
{
let somethingElse : FactoryObject = template!.dynamicType()
return somethingElse
}
return nil
}
}
import ObjectiveC
class FactoryObject : NSObject
{
#required init() {}
//...
}
class Foo : FactoryObject
{
class override func initialize()
{
mapClass("LocalData", LocalData())
}
init () { super.init() }
}
var makeFoo : AnyObject? = Factory.create("Foo")
and bingo, "makeFoo" contains a Foo instance.
The downside is your classes must derrive from FactoryObject and they MUST have the Obj-C +initialize method so your class gets automagically inserted in the class map by global function "mapClass".
Here is another example showing class hierarchy implementation, similar to accepted answer, updated for the first release of Swift.
class NamedItem : NSObject {
func display() {
println("display")
}
required override init() {
super.init()
println("base")
}
}
class File : NamedItem {
required init() {
super.init()
println("folder")
}
}
class Folder : NamedItem {
required init() {
super.init()
println("file")
}
}
let y = Folder.self
y().display()
let z = File.self
z().display()
Prints this result:
base
file
display
base
folder
display

Resources