I am trying to add a static variable and a static function to all instances of a class and its child classes using the #:build and #:autoBuild macros.
I managed to get the static variable working, but I have no idea how to "build" the function from the various EFunction, EFor, etc.
Here is the code I have so far:
macro static public function addGetId() :Array<Field>
{
var fields : Array<Field> = Context.getBuildFields();
// The static _id field
var idField = {
name : "_id",
doc : null,
meta : [],
access : [AStatic, APrivate],
kind : FVar(macro : Int, macro -1),
pos : Context.currentPos()
};
// The getId function field
var getIdField = {
name : "getId",
doc : "Returns the ID of this command type.",
meta : [],
access : [AStatic, APublic],
kind : FFun({
params : [],
args : [],
expr: // What do I have to write here???
ret : macro : Int
}),
pos : Context.currentPos()
};
fields.push(idField);
fields.push(getIdField);
return fields;
}
Here is how the function that I want to add would look like in normal code, if it was actually in the .hx file:
public static function getId() : Int
{
if (_id == -1)
{
_id = MySingleton.getInst().myGreatFunction()
}
return _id;
};
So it references the newly added _id variable as well as some singleton class function.
So: How would the complete getIdField() look like?
Bonus Question:
My biggest problem with this is the complete lack of documentation on these features as well as any useful examples in the manual. Is there any actually useful tutorial for creating functions like this?
Bonus Bonus Question:
What is the difference between params and args in FFun?
You can make use of reification to write the body of the function like you would in regular Haxe code:
expr: macro {
if (_id == -1) {
_id = 0;
}
return _id;
},
params is a list of type parameters, args is the list of arguments the function receives. There's a trivia section about this on the Haxe Manual:
Trivia: Argument vs. Parameter
In some other programming languages, argument and parameter are used interchangeably. In Haxe, argument is used when referring to methods and parameter refers to Type Parameters.
Related
I'm exploring ReScript for the first time. I want to build a hashmap using a record type as my key type, and I'm looking for guidance on implementing the hash function.
Here's my ReScript code:
type pointer = { id: string, table: string, spaceId: option<string> }
module PointerHash = Belt.Id.MakeHashable({
type t = pointer
let hash = a => 0 /* How do I implement this? */
let eq = (a, b) =>
a.id == b.id &&
a.table == b.table &&
switch (a.spaceId, b.spaceId) {
| (Some(aid), Some(bid)) => aid == bid
| _ => false
}
})
I looked through the docs and searched online but didn't find any guidance about how to implement the hash function.
In other programming languages like eg Java that expect you to implement hashCode(), there are ubiquitous tools to support composing existing hash functions.
class Pointer {
public final id: String
public final table: String
public final #Nullable spaceId: String
/* omitting constructor, etc */
#Override
public int hashCode() {
// Standard helper for implementing hashCode()
return Objects.hash(this.id, this.table, this.spaceId);
}
}
I looked at the implementation of Belt.HashMapString to see if there were any hints, and it looks like HashMapString uses caml_hash_mix_string:
external caml_hash_mix_string : seed -> string -> seed = "caml_hash_mix_string"
external final_mix : seed -> seed = "caml_hash_final_mix"
let hash (s : key) =
final_mix (caml_hash_mix_string 0 s )
What is the most idiomatic way to access and compose "hash mix" functions? Are these available with a nice interface from ReScript?
There's a built-in polymorphic hash function in the Hashtbl module:
let hash: 'a => int
This comes from the OCaml standard library which ReScript inherits. You can find the documentation there: https://docs.ocaml.pro/html/LIBRARY.stdlib#ocaml-base-compiler.4.10.0/Stdlib/Hashtbl/index.html#val-hash
For purposes of testing, it's useful to be able to "prepare" arguments to a function before executing such that the arguments can be checked against any result.
In JavaScript I can do this:
function testFunc ({id, count}) {
/* perform some operation */
return {id, count}
}
const args = {
id: 'someId',
count: Math.round(Math.random() * 10),
}
const res = testFunc({...args})
/* check that count is correct etc */
How do I achieve the same flexibility in Dart?
Map<String, dynamic> testFunc({String id, int count}) {
/* perform some operation */
return {
'id': id,
'count': count,
};
}
final args = /* erm? */
testFunc(args); /* hmmm? */
Am I trying to push the limits of a strongly typed language?
Dart is a statically checked language.
Using a value with an unknown run-time structure, like a map, as parameters makes it impossible to statically check the validity of the arguments.
Take the example:
var data = {#name: "hello", #age: 18};
Person(...data)
(Using symbols for referring to source names, same as Function.apply and noSuchMethod).
Here it looks easy to see that the map actually has a #name and an #age entry, but that's because the map is written as a literal right next to the application. That's the one situation where you don't actually need spread arguments, because you could just write the arguments directly.
In all actually useful cases, it's not possible to see statically which entries the map has, and which types the values are for each key. The type of the map, Map<Symbol, dynamic> is not strong enough to allow checking the call. You should just use Function.apply(Person, [], data) ... except that we (still) do not allow constructors as functions (#216).
If Dart had typed structs/named tuples, it might be possible to do:
// Static type is the named tuple type `(String name, int age)`
var data = (name: "name", age: 18);
var person = Person(...data);
At this point, the static type of data is specific enough to allow the call to be checked statically.
also this is issue in GitHub which I take the answer from
I have a LangBuilder macro class; it's used to build a langObjects:Map<String, Dynamic> of texts in various languages at compile time, and inject this structure in classes via #:build macro. Every item of the Map has a field for every language supported. So the result is:
#:build(LangBuilder.build())
class Lang{}
trace(Lang.langObjects["hello-world"].en); //outputs "Hello World!"
trace(Lang.langObjects["hello-world"].it); //outputs "Ciao Mondo!"
This works perfectly, but I thought I could make a cleaner job hiding the langObjects structure using a function getLangText with arguments the id of the text (e.g. "hello-world") and the language code (e.g. "it").
So I'm trying to add this function to classes:
public static function getLangText(id:String, lang:String)
Its non-macro version could be expressed as:
public static function getLangText(id:String, lang:String){
var _langObj_id = langObjects[id];
switch(lang){
case "it":
return _langObj_id.it;
case "en":
return _langObj_id.en;
}
return "Missing Translation";
If i translate this function as a macro with this code:
var code = macro {
var _langObj_id = langObjects[$i{"id"}];
switch($i{"lang"}){
case "it":
return _langObj_id.it;
case "en":
return _langObj_id.en;
}
return "Missing translation";
};
var myFunc:Function = {
args: [{
name: "id",
type: TPath({name: "String", pack: []}),
meta: null
},
{
name: "lang",
type: TPath({name: "String", pack: []}),
meta: null
}],
ret: (macro:String),
expr: macro $code
};
fields.push({
pos: Context.currentPos(),
name: "getLangText",
meta: null,
kind: FieldType.FFun(myFunc),
doc: null,
access: [Access.APublic, Access.AStatic]
});
... it works without problems. However I would like to know how it could be written without the switch, to make it more flexible and to learn something about haxe macros. I have seen some examples where fields could be accessed in macros with $p{} or with object.$fieldName. However the haxe manual warns that the second form could be used only for simple identifiers; for example object.${fieldName} would not work.
So I try this code:
var code = macro {
var l:String = $i{"lang"};
var _langObj_id = langObjects[$i{"id"}];
return _langObj_id.$l;
};
The compiler gives an error
Unknown identifier : l
on the line containing return _langObj_id.$l;.
Then i tried to use the $p{} reification:
var code = macro {
var _langObj_id = langObjects[$i{"id"}];
return macro $p{["_langObj_id", $i{"lang"}]};
};
But the error is similar:
Unknown identifier : lang
I can surely change the langObjects structure to Map<String, Map<String, String>> and then change the code to:
var code = macro {
return macro langObjects[$i{"id"}][$i{"lang"}];
};
I think this would work, but now i'm trying to understand why both _langObj_id.$lang and $p{["_langObj_id", $i{"lang"}]} wouldn't work, and what would be the correct way to access a field in a situation like that.
The value of the lang parameter is not known at compile- / macro-time, so I don't see how you could generate a field access expression like langObjects["mytext"].en. At runtime when getLangText() is actually called, lang could be "en", or anything else. So that would still require a switch-case, if-else-chain or reflection to handle all the possible values.
If instead of using being created by a build macro, getLangText() was an expression macro / a macro function, the function call would be evaluated at compile-time, and be replaced with the expression it returns. That would allow you to generate the appropriate field access expression based on the parameters. It could look something like this:
class Macro {
public static var langObjects = ["mytext" => {en: "hello", de: "hallo"}];
public static macro function getLangText(id:String, lang:String) {
return macro {
var langObject = Macro.langObjects[$v{id}];
langObject.$lang;
}
}
}
class Main {
static function main() {
trace(Macro.getLangText("mytext", "en"));
trace(Macro.getLangText("mytext", "de"));
}
}
Compiles to the following on the JS target:
Main.main = function() {
var langObject = Macro.langObjects.get("mytext");
console.log("source/Main.hx:3:",langObject.en);
var langObject1 = Macro.langObjects.get("mytext");
console.log("source/Main.hx:4:",langObject1.de);
};
Perhaps that's what you're looking for? Hard to say without knowing what problem you're trying to solve.
in my verification environment we work with vr_ad UVM package, where there is a general struct for a register vr_ad_reg which has been extended with different type for every register in the environment, etc:
reg_def TIMER_LOAD_0 TIMER 20'h00010 {
reg_fld timer_load : uint : RW : 0xffff;
}:
The vr_ad_reg has predefined function post_access(), which I would like to extend for every register type that starts with the word 'TIMER'. Is there a way to do it? For example:
extend TIMER_* vr_ad_reg { //The intention here to extend the vr_ad_reg for all types that starts with the word TIMER
post_access() is also {
var some_var : uint;
};
}
Thank you for your help
There's no built in construct to extend multiple sub-types. What you can do however is use a macro based solution. Team Specman had a blog post on this topic: http://www.cadence.com/Community/blogs/fv/archive/2009/10/20/extending-multiple-when-subtypes-simultaneously.aspx
They created a define as computed macro that takes multiple sub-types and extends those:
define <multi_when'statement> "extend \[<detr'name>,...\] <base'type> (<MEMBERS {<struct_member>;...})" as computed {
for each in <detr'names> do {
result = appendf("%s extend %s %s %s;",result,it,<base'type>,<MEMBERS>);
};
};
You can then use like so:
extend [ TIMER_LOAD_0, TIMER_LOAD_1, TIMER_LOAD_2 ] vr_ad_reg {
post_access() is also {
// ...
};
};
If you have a lot of registers that match your expression or you don't know the exact name beforehand, you might want to consider using a run-time solution:
extend vr_reg {
post_access() is also {
var some_var: uint;
if str_match(kind.as_a(string), "/^TIMER_*/") {
... // do stuff for the TIMER_* registers
};
};
};
Is it possible to get class of field with null value in haxe?
The function "Type.getClass" gets class of value (setted at runtime), but I need to get class defined in a compilation-time.
Function "getClassFields" returns only names of fields, without classes.
For example:
class MyCls
{
public static var i:Int = null;
public static var s:String = null;
}
trace(Type.getClass(MyCls.i)); // show "null", but I need to get Int
trace(Type.getClass(MyCls.s)); // show "null", but I need to get String
And in my situation I can't to change sources of class MyCls.
Thanks.
You can try Runtime Type Information. It's a Haxe feature which allow go get full description of a type in runtime.
http://haxe.org/manual/cr-rtti.html
Since you need to get the types for null fields, you really need to resort to Haxe's Runtime Type Information (RTTI) (as #ReallylUniqueName recomended).
import haxe.rtti.Rtti;
import haxe.rtti.CType;
class Test {
static function main()
{
if (!Rtti.hasRtti(MyCls))
throw "Please add #:rtti to class";
var rtti = Rtti.getRtti(MyCls);
for (sf in rtti.statics)
trace(sf.name, sf.type, CTypeTools.toString(sf.type));
}
}
Now, obviously, there's a catch...
RTTI requires a #:rtti metadata, but you said you cannot change the MyCls class to add it. The solution then is do add it through a macro in your build file. For instance, if you're using a .hxml file, it should then look like:
--interp
--macro addMetadata("#:rtti", "MyCls")
-main Test
With this and your own MyCls definition, the output would look like:
Test.hx:11: i,CAbstract(Int,{ length => 0 }),Int
Test.hx:11: s,CClass(String,{ length => 0 }),String