How to pass null argument to function that accept :xhp as parameter? - hacklang

I have function like this :
private static function myfun(
string $param1,
:xhp $param2,
): :xhp {
return
//somethinf
}
I don't want to pass any thing as param2. How can I do that ?
When I try doing like :
myfun("Hi",null),
It shows me error.

To be able to pass null in, your type hint has to allow it. In Hack, this is done by using a nullable type.
private static function myfun(
string $param1,
?:xhp $param2,
): :xhp {
return
//somethinf
}
The Hack type checker will then also make sure that you check $param2 is not null before using it.

Does this the trick?
private static function myfun(
string $param1,
:xhp $param2=null,
): :xhp {
return
//somethinf
}

Related

How to use #ClosureParams for closure that expects two parameters?

I had a method that have a closure as one of its parameters and I wished to cast the input closure's param with the #ClosureParam annotation:
def <T> List<T> randomInstances( final int size, final Builder<T> builder,
#ClosureParams( SecondParam.FirstGenericType )
final Closure<Void> postProcessor = null ) {
( 0..<size ).collect {
def instance = builder.build()
if ( postProcessor ) {
postProcessor( instance )
}
instance
}
}
Now, I've added the second method that will do the same, but the closure will receive two params:
def <T> List<T> randomInstances( final List<?> listToIterate,
final Builder<T> builder,
#ClosureParams( FirstParam.FirstGenericType )
#ClosureParams( SecondParam.FirstGenericType )
final Closure<Void> postProcessor = null )
I'm pretty sure that two #ClosureParams in line it's wrong use case. But I've not found the way how to pass two closure's parameters "declaration" to the method signature.
Is it possible?? Could anybody help??
P.S. When I used the annotation, I had expect not only describing for future reader, but also to help IDEA to infer the param's type. But I didn't get that result?? What I do wrong, or IDEA just don't support this feature??
In this case you can use groovy.transform.stc.FromString with ["T,U", "T"] options:
#ClosureParams(value = groovy.transform.stc.FromString, options = ["T,U", "T"])
And here is a short example:
class Lists {
static <T,U> List<T> randomInstances(List<U> listToIterate, final Builder<T> builder, #ClosureParams(value = FromString, options = ["T,U", "T"]) final Closure<T> postProcessor = null) {
(0..<listToIterate.size()).collect {
def instance = builder.build()
if (postProcessor) {
postProcessor(instance)
}
instance
}
}
}
And here is what what IDE param suggestion looks like for given method definition:

have completion for #:op(a.b)

The #:op(a.b) feature is described here: https://haxe.io/releases/3.3.0/
I have May<T> abstract which is used for null safety. Here is the simplified version of it:
package;
import haxe.macro.Expr;
abstract May<T>(Null<T>) from(Null<T>){
// convert T, Null<T> or May<T> to May<T>
// accepts Null<T because of 'from(Null<T>)'
// accepts T because Null<T> has 'from(T)'
// we need to accept May<T> to avoid returning May<May<T>> from resolve() if field is already a May
public static inline function from<T>(t:May<T>):May<T> return t;
public inline function exist():Bool return this != null;
public inline function unwrap():T return exist() ? unsafeUnwrap() : throw 'unwrap null';
public inline function unwrapOr(defaultValue:T):T return exist() ? unsafeUnwrap() : defaultValue;
public inline function unsafeUnwrap():T return this;
// return same field from underlying type but as May<FieldType>
#:op(a.b) static macro function resolve<T>(ethis:ExprOf<May<T>>, name:String):Expr {
return macro {
var me = $ethis;
var result = May.from(me.exist() ? me.unsafeUnwrap().$name : null);
result;
}
}
}
Note the resolve() function. It's the new feature that I want to add to my actual May abstract. It allows to safely get fields from May and call unwrap() only once. For example:
may.exist() ? may.unwrap().someField : defaultValue
becomes
may.someField.unwrapOr(defaultValue)
That's very handy and works good. But the completion does not work. It only gives fields from May: unwrap(), exist() etc., but no fields from the underlying class.
I've decided to add #:forward metadata for completion:
#if display #:forward #end
This makes the compiler see all fields during completion. It's better than nothing, but fields have an incorrect type: T instead of May<T>, so I do not get completion for May fields.
I understand why the compiler can't know all possible fields when using #:op(a.b), but maybe there is some more clever trick that will help?

In Haxe, how do you pass Enum values in functions, and then convert them to Strings within the function?

I can't seem to get this working, but I'd be surprised if it wasn't possible in Haxe.
I'm trying to pass a couple of Enum values defined in my game to a function, so that it can then concatenate them as String types and pass that to other functions.
Example:
// In a general Entity class:
public override function kill():Void {
messages.dispatchCombined(entityType, ListMessages.KILLED);
super.kill();
}
And in my Messages.hx class:
package common;
import msignal.Signal.Signal1;
/**
* A Message / Event class using Signals bound to String names.
* #author Pierre Chamberlain
*/
class Messages{
var _messages:MessagesDef;
public function new() {
_messages = new MessagesDef();
}
public function add(pType:String, pCallback:FuncDef) {
if (_messages[pType] == null) {
_messages[pType] = new Signal1<Dynamic>();
}
var signals = _messages[pType];
signals.add( pCallback );
}
public function dispatch(pType:String, pArg:Dynamic):Bool {
var signals = _messages[pType];
if (signals == null) return false;
signals.dispatch(pArg);
return true;
}
//Compiler doesn't like passing enums :(
public inline function addCombined(pSource:Enum, pEvent:Enum, pCallback:FuncDef) {
add( combine(pSource, pEvent), pCallback );
}
public inline function dispatchCombined(pSource:Enum, pEvent:Enum, pArg:Dynamic):Bool {
return dispatch( combine(pSource, pEvent), pArg);
}
//How can I just pass the enum "names" as strings?
static inline function combine(a:Enum, b:Enum):String {
return String(a) + ":" + String(b);
}
}
typedef MessagesDef = Map<String, Signal1<Dynamic>>;
typedef FuncDef = Dynamic->Void;
Note how addCombined, dispatchCombined and combine expect an "Enum" type, but in this case I'm not sure if Haxe actually expects the entire Enum "class" to be passed (ie: ListMessages instead of ListMessages.KILLED) or if a value should work. Anyways, compiler doesn't like it - so I'm assuming another special Type has to be used.
Is there another way to go about passing enums and resolving them to strings?
I think you need EnumValue as parameter type (if it is only for enum values), and use Std.String to convert to String values.
static inline function combine(a:EnumValue, b:EnumValue):String {
return Std.string(a) + ":" + Std.string(b);
}
Of course that can be written smaller using String interpolation:
static inline function combine(a:EnumValue, b:EnumValue):String {
return '$a:$b';
}
Of course that can be 'more dynamic' using type parameters:
static inline function combine<A, B>(a:A, b:B):String {
return '$a:$b';
}
There is totally no need to use Dynamic as suggested. If you use Dynamic, you basically turn off the type system.
live example:
http://try.haxe.org/#a8844
Use Dynamic instead of Enum or pass them as Strings right away since you can always convert to enum from String if you need it later.
Anyway pass the enum as enum:Dynamic and then call Std.string(enum);
EDIT: Using EnumValue is definitely better approach than Dynamic, I use Dynamic in these functions because I send more than just Enums there and I am not worried about type safety in that case.

Overriding parent methods with contravariant arguments

Basically, I want to override a parent class with different arguments. For example:
class Hold<T> {
public var value:T;
public function new(value:T) {
set(value);
}
public function set(value:T) {
this.value = value;
}
}
Then override that class, something like:
class HoldMore extends Hold<T> {
public var value2:T;
public function new(value:T, value2:T) {
super(value);
set(value, value2);
}
override public function set(value:T, value2:T) {
this.value = value;
this.value2 = value2;
}
}
Obviously this will return an error, Field set overloads parent class with different or incomplete type. Is there a way around this? I tried using a public dynamic function, and then setting set in the new() function, but that gave a very similar error. Any thoughts?
This is just a complement to #stroncium's answer, which is totally correct.
Here is an example how it could look like:
class Hold<T> {
public var value:T;
public function new(value:T) {
set(value);
}
public function set(value:T) {
this.value = value;
}
}
class HoldMore<T> extends Hold<T> {
public var value2:T;
public function new(value:T, value2:T) {
super(value);
setBoth(value, value2);
}
// you cannot override "set" with a different signature
public function setBoth(value:T, value2:T) {
this.value = value;
this.value2 = value2;
}
}
alternatively, you could use an array as parameter or a dynamic object holding multiple values in order to "set" them using the same method, but you loose some of the compiler's type checking.
If you wrote the base class you could add an optional argument to it, this would be a workaround though, not directly what you want to do.
In the current state it totally won't work. There is not only 1 problem, but few of them:
Type T is meaningless in context of this new class, you should either use some concrete type or template this class over T.
You can not change the number of arguments of function when overriding it. However you can add another function(with a different name) to accept 2 arguments and do what you want (which is the way you would use in most languages, by the way).
I don't really understand how you see a contravariance problem there. The actual problem is that haxe doesn't support function overload. (It actually does, the function signature is name + full type, but that's not what you would want to write nor support, and is mostly used for js/java externs.)
Unfortunately the language doesn't allow it.

Overloading a method which accepts `object` as default parameter type

I need to be able to call a method and pass in an object of an unknown type
but then have the correct overload called. I also need a default implementation that accepts
object as its parameter type. What I'm seeing is that the default overload is the only one that ever gets used.
Here's the gist of what I'm trying to do:
class Formatter
{
private object Value;
public Formatter(object val){
Value = val;
}
public override string ToString()
{
return Format(Value);
}
private string Format(object value)
{
return value.ToString();
}
private string Format(DateTime value)
{
return value.ToString("yyyyMMdd");
}
}
Ok, so far so good. Now I want to be able to do this:
public static class FancyStringBuilder()
{
public static string BuildTheString()
{
var stringFormatter = new Formatter("hello world");
var dateFormatter = new Formatter(DateTime.Now);
return String.Format("{0} {1}", stringFormatter, dateFormatter);
}
}
The result of FancyStringBuilder.BuildTheString() is "hello world 2012-12-21 00:00:00.000", when I expected "hello world 20121221"
The problem is that the overload that accepts a DateTime is not being called, instead defaulting to the overload which accepts an object. How can I call the proper method without resorting to a messy switch statement?
In Formatter.ToString(), the override Formatter.Format(object) is always called. This is because the overload resolution happens at compile-time, not run-time. At compile-time, the only thing known about Value is that it's an object.
If you really want to distinguish incoming types, you'll need to do so in Formatter's constructor. In this case, rather than hanging on to the object, you could just call ToString() immediately and only store the formatted result:
class Formatter
{
string formattedValue;
public Formatter(object value)
{
formattedValue = value.ToString();
}
public Formatter(DateTime value)
{
formattedValue = value.ToString("yyyyMMdd");
}
public string ToString()
{
return formattedValue;
}
}
Note that this does assume that your object isn't changing between the time you create the Formatter object and the time Formatter.ToString() is called, or at the very least that it's okay to take a snapshot of the string representation at the time the Formatter is created.
This also assumes that you know the incoming types at compile-time. If you want a truly run-time-only solution, you'll have to use the "is" operator or a typeof() comparison.
If your goal is just to provide custom ToString() formatting based on the incoming type, I'd probably do it using a list that maps from types to format strings:
static class Formatter
{
private static List<Tuple<Type, string>> Formats;
static Formatter()
{
Formats = new List<Tuple<Type, string>>();
// Add formats from most-specific to least-specific type.
// The format string from the first type found that matches
// the incoming object (see Format()) will be used.
AddMapping(typeof(DateTime), "yyyyMMdd");
// AddMapping(typeof(...), "...");
}
private static void AddMapping(Type type, string format)
{
Formats.Add(new Tuple<Type, string>(type, format));
}
public static string Format(object value)
{
foreach (var t in Formats)
{
// If we find a type that 'value' can be assigned to
// (either the same type, a base type, or an interface),
// consider it a match, and use the format string.
if (t.Item1.IsAssignableFrom(value.GetType()))
{
return string.Format(t.Item2, value);
}
}
// If we didn't find anything, use the default ToString()...
return value.ToString();
}
}
With that, calling code then looks like:
Console.WriteLine(
"{0} {1}",
Formatter.Format(DateTime.Now),
Formatter.Format("banana"));
I think this is because the class constructor takes an object as parameter, and then assign that object to variable Value which is also an object. There for calling Format(object) since Value is of type object
Try this
public override string ToString()
{
if(Value is DateTime)
return Format(Convert.ToDateTime(Value)); //this should call the right method
return Format(Value); //works for other non-custom-format types e.g. String
}

Resources