Accept either String or Int, without resorting to Dynamic

A quick code sample:

`AcceptEither`, a way to accept either one type or another, without resorting to “Dynamic”, and still have the compiler type-check everything and make sure you correctly handle every situation.

Try it on try.haxe.org

The next step would be having Accept2Types, Accept3Types etc, and have a macro automatically build the code for whichever option you ask for.

6 thoughts on “Accept either String or Int, without resorting to Dynamic

  1. Cool! although it seems black magic inside :-p, but definitely useful.

    I have a function that returns Float, some times I need to assign it to an Int variable, I end up Std.int all the time like:

    var a:Int= Std.int(floatFunction());

    What do you recommend for my case? is it possible that I get casting on the fly without using Dynamic?

    • Hi Samir

      You could create an abstract like this:

      abstract IntCaster(Int) from Int to Int {
      @:from static public inline function fromFloat( f:Float ) return Std.int( f );
      }

      And then in your code:

      var a:IntCaster = 3;
      var b:IntCaster = 3.14;
      a; // 3
      b; // 3

      or

      function takeInt( i:IntCaster ) { trace( i ); }

      takeInt( 3.14 ); // Will trace “3”, the cast will work automatically.

      This works because `IntCaster` wraps an Int, so at runtime, it is always stored as an Int. Because it has “from Int” and “to Int”, it will accept ints and give ints wherever required.

      When you try to assign `var b:IntCaster = 3.14`, it realizes you have a float, and checks for a `@:from` auto-casting function that turns a float into an int.

      If this is all confusing, create an `IntCaster` like I have above, do a simple test, and compile to Javascript, then take a look at the resulting code to see how abstracts really work :)

    • Glad you like it!

      There is another post about the next logical step, which is “trampoline types” that Juraj introduced me to. I’m still waiting for time to write that one… Hopefully soon!

  2. Jason – one thing. Playing with this awesome example at try.haxe.org, I notice that you can only use AcceptEither with one parameterization… That is, if you try to define a second function, boolOrInt( either:AcceptEither), the compiler won’t take calls with the different types.

    Then I noticed that your fromA and fromB actually need to return AcceptEither types:

    @:from static function fromA( v:A ) return new AcceptEither( Left(v) );
    @:from static function fromB( v:B ) return new AcceptEither
    ( Right(v) );

    With that, you can parameterize as many functions with various AcceptEither’s as you want.

    One more note, many types will accept null, so you’ll probably want to null-check the either parameter:

    if (either==null) trace(“null!”) else switch either.type

    Awesome example, thanks!

    • Good point! I’ve changed the try.haxe.org example to include a boolOrInt function and you were right. I added some extra return type hints and the Haxe compiler figured itself out.

      As for the null values – I can’t wait until Haxe has better support for requiring explicit checks on `Null`. Remembering to test in every location gets pretty old pretty quickly!

Leave a Reply

Your email address will not be published. Required fields are marked *