F# attributes, typeof, and "This is not a constant expression" - attributes

EDIT: Added a more complete example, which clarified the problem.
Some .NET attributes require a parameter of type Type. How does one declare these parameters in F#?
For example, in C# we can do this:
[XmlInclude(typeof(Car))]
[XmlInclude(typeof(Truck))]
class Vehicle { }
class Car : Vehicle { }
class Truck : Vehicle { }
But, in F# the following...
[<XmlInclude(typeof<Car>)>]
[<XmlInclude(typeof<Truck>)>]
type Vehicle() = class end
type Car() = inherit Vehicle()
type Truck() = inherit Car()
...results in a compiler error: This is not a constant expression or valid custom attribute value.

You should address a circular type dependency introduced by forward usage of types in attributes. The snippet below shows how this can be done in F#:
// Compiles OK
[<AttributeUsage(AttributeTargets.All, AllowMultiple=true)>]
type XmlInclude(t:System.Type) =
inherit System.Attribute()
[<XmlInclude(typeof<Car>)>]
[<XmlInclude(typeof<Truck>)>]
type Vehicle() = class end
and Car() = inherit Vehicle()
and Truck() = inherit Car()

Can you try putting together a more complete example that gives the error? I just quickly tried something similar and it works fine (in F# 3.0 in Visual Studio 2012):
type Car = C
type XmlInclude(typ:System.Type) =
inherit System.Attribute()
[<XmlInclude(typeof<Car>)>]
let foo = 0
I guess there is some tiny detail somewhere that confuses the F# compiler for some reason - but it should understand typeof (which is, in reality, a function) and allow its use in attributes.

Related

Explain #:remove #:generic metadata combination

Consider class with type parameter, that should be used as Map key.
class Foo<K> {
var map: Map<K, Dynamic> = new Map();
}
This does not compile with error Type parameters of multi type abstracts must be known.
The reason is understandable - Map is abstract and it's underlying type is selected based on key type, so key type should be known when compiling new Map() expression. On the other hand non-generic type with type parameters are compiled once for all parameters.
Looks like adding #:generic metadata should help. But actually it does not. My guess was that this is because haxe compiler compiles #:generic types the same as it does with non-generic types, and only then does some extra work for generic type. So I was thinking that having a Map with key type defined by type parameter is impossible in haxe.
But recently I've stumbled upon this issue: https://github.com/HaxeFoundation/haxe/issues/2537
Simn's answer there says, that this can be done by adding #:remove #:generic metadata. And it actually works.
#:remove #:generic
class Foo<K> {
var map: Map<K, Dynamic> = new Map();
}
For me this looks like magic and I'm not comfortable with this. In documentation I only see that
#:remove Causes an interface to be removed from all implementing classes before generation. That does not explain why this works.
If you replace Map by haxe.ds.BalancedTree and use only #:generic without #:remove, you will see your class Foo generated on your target containing new BalancedTree< object, object > (object is Dynamic). That means, Foo class holds general BalancedTree< object, object > type, while Foo_String for example holds generic BalancedTree< string, object > type. Now, if you go back to your Map, you will see that "Abstract haxe.ds.Map has no #:to function that accepts haxe.IMap< Foo.K, Dynamic >", e.g. no Map< Dynamic, Dynamic > implementation exists. That's why you need to use #:remove which will actually stop generation of Foo class, or at least, remove new Map< Dynamic, Dynamic > from the Foo (#:remove "Causes an interface to be removed" is misleading here)

Inconsistent Accessibility of Nested object Declarations in Kotlin

Consider the following Kotlin code:
object Domain {
val name = "local"
val location = object {
val city = "Pittsburgh"
val state = "Pennsylvania"
}
}
While this definition is valid and compiles, the following line fails:
val x = Domain.location.city // Error:(30, 27) Kotlin: Unresolved reference: city
However, if we rewrite the above definition as follows:
object City {
val city = "Pittsburgh"
val state = "Pennsylvania"
}
object Domain {
val name = "local"
val location = City
}
val x = Domain.location.city // works fine
My question: is this really correct behavior according to the language spec? This doesn't seem sensible or consistent. It makes it inconvenient to declare complex singleton object declarations without breaking each child object out into a top-level declaration. It seems like the compiler is creating anonymous types when this syntax is used, however, type of the assigned value is always object when the definition is in a nested context. This almost seems like a type inference bug in the compiler. What am I missing?
What creating an anonymous object does is create a class that extends Any. Since no type information can be given except the superclass (in this case, Any), all fields not declared in that superclass will not be visible.
By creating your named object Bar, the class Bar is generated, as are the references to the fields, which is why they are visible in that case.

ThisIsNotAObjectReference error when accessing component of returning parameter

I'm getting ThisIsNotAObjectReference syntax message when trying to handle a fully typed data reference expression like this:
method getDataReference.
rr_value = ref #( varStructure ).
endmethod.
data(lr_value) = object->getDataReference( )->structureComponent.
I understand the syntax is identical for retrieving object references but how to deal with fully typed data references in expressions?
You cannot chain method calls with reference component accesses. You should use auxiliary variable like here:
CLASS lcl_class DEFINITION.
PUBLIC SECTION.
METHODS: getdatareference RETURNING VALUE(rr_value) TYPE REF TO mara.
ENDCLASS.
CLASS lcl_class IMPLEMENTATION.
METHOD getdatareference.
DATA: ls_mara TYPE mara.
rr_value = REF #( ls_mara ).
SELECT SINGLE * FROM mara INTO rr_value->*.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(lcl) = NEW lcl_class( ).
DATA(lv_mara) = lcl->getdatareference( ).
DATA(l_matnr) = lv_mara->matnr.
I'm kind of speculating about what you're trying to achieve here but as far as I know you'll need to dereference data reference before you can access their contents. The following extends the code you posted above.
report ztest.
class lcl_test definition.
public section.
methods getDataReference
returning value(rr_value) type ref to tadir.
private section.
data varStructure type tadir.
endclass.
class lcl_test implementation.
method getDataReference.
varstructure-author = sy-uname.
rr_value = ref #( varStructure ).
endmethod.
endclass.
start-of-selection.
data(object) = new lcl_test( ).
data(lr_value) = object->getDataReference( ).
field-symbols <structure> type tadir.
assign lr_value->* to <structure>. " This is the dereferencing step
write / <structure>-author.

c# cast a value obtained from FieldInfo

Grade in an Enum Structure.
var y=1;
var x= (Grade)y;
I'm trying to do the same thing as the above line but with a dynamic CLASSNAME.
FieldInfo field = typeof(Person).GetField("Grade");
var x= Convert.ChangeType(y ,field.FieldType);
I tried that. this works fine but not for enums.
I think the problem is with the way you are accessing the field on the enum. Enum fields are static. By default, the Type.GetField method uses binding flags equivalent to BindingFlags.Public|BindingFlags.Instance. This won't match an enum member.
If this is the problem you are having, then you can use typeof(Person).GetField("Grade",BindingFlags.Public|BindingFlags.Static) to get the FieldInfo for the field named "Grade" on the enum type named "Person". This assumes that your model looks like:
enum Person
{
Grade
}
There's another problem with your code that is compatible with enums as well. It's not totally obvious because your example seems to treat "Grade" as both a field and an type. If my previous suggestion doesn't describe your model, and the following does, then the problem is that you are using Convert.ChangeType which in this case should give you an InvalidCastException.
You need to find a different way to cast the value to your enum. If the class name is not known at compile-time, then I would suggest using linq expressions, e.g.
Type targetEnumType = typeof(Person).GetField("Grade");
ConstantExpression runtimeValue = Expression.Constant(y);
UnaryExpression cast = Expression.Convert(runtimeValue,targetEnumType);
LambdaExpression lambda = Expression.Lambda(cast);
Delegate getTheCastValue = lambda.Compile();
object value = getTheCastValue.DynamicInvoke();
This code assumes that your model looks something like
class Person
{
public Grade Grade;
}
enum Grade
{
First = 1,
Second = 2
}
However, looking at this, it becomes obvious that if Person is a non-generic class, then you would have to know the type of the Grade field at runtime, so you are better off just doing a (Grade)y cast, like in your example.

Vala: Pass String as Class

Scenario:
I have x number of classes. Lets say 10; Each class does different UI Functions. When a user loads a file, that extension tells the program the classname to load; but it's in the form of a string.
Is there anyway to pass a string off as a classname? Something to the effect of.
var classname = "Booger";
var nose = new classname(){ //classname really means "Booger"
//Do Operation
}
You can reflect a type by name using var t = Type.from_name(classname);, however, this works on all types, including enums and structs and it might be the type Type.INVALID. You should probably do some checks, like t.is_a(typeof(MyParentClass)).
You can then instantiate a copy using var obj = Object.new(t);. The whole thing would look like:
var classname = "Booger";
var t = Type.from_name(classname);
if (t.is_a(typeof(MyParentClass)))
return Object.new(t);
else
return null;
It's also worth noting that the run-time type names have the namespace prepended, so you might want to do "MyNs" + classname. You can check in either the generated C or doing typeof(MyClass).name().
I've had the same problem as the OP in regards to getting an assertion error against null. If you take a look at the Glib documentation (in C) it mentions you have to register your class by actually specifying the class name first before you can actually use a string representation of your class name.
In other words you have to use your class first BEFORE you can instantiate a copy of your class with Glib.Type.from_name ("ClassName").
You can use your class first by instantiating a class instance or by getting type information for your class.
var type = typeof (MyClass);
var type_from_string = Type.from_name ("MyClass");
Furthermore, when you use Object.new to create a class there are two things you need to be aware of:
1) You need to cast the return value to get your specific class or base class.
var instance_of_my_class = Object.new (type) as MyClass;
2) Constructors for your class will no longer be called (I don't why). You will need to use the GObject style constructor inside your class:
construct {
pizza = 5;
}

Resources