Can I define an implicit conversion to String into a non-abstract class?

In the example below, I’d like the trace to print out something.

I defined a toString method but obviously that doesn’t work… I’m missing some basics here I think :slight_smile:

class Test {
    static function main() {
        trace(new TestStringPush());
    }
}

class TestStringPush
{
  public function new()
  {
    
  }
  
  public function toString(): String
  {
    return "something";
  }
}

Not really. It should work :slight_smile: In that particular try-haxe example the toString method is being eliminated by dead code elimination (DCE) as it’s not used directly anywhere, so you might need to add a @:keep to it. However, I believe if you have Std.string in your compilation, it’ll be kept automatically.

1 Like

Hah! Indeed, I’m not calling that method in my (more complex) project. Thank you, Dan! :slight_smile:

Hm, I’d like the Haxe compiler to realize that I’m using the toString. When a Type is part of a string operation and it has a toString method, use it and flag it to not be removed? Perhaps now I am missing something about how the compiler works or some architectural decision that was made for our sake :slight_smile:

Oh, and a follow-up question: how can I then do this?

Array<String>.push(new TestStringPush());

Does Explicit create of the class.
Does an Explicit call to toString();

Just wraps the new call inside a call to Std.string to put in your string Array.
“still Dangerous with Strings”

1 Like

@RMax - i dont think that is what hes asking, how you implicitly perform that without std.string or .toString. You could use cast but thats pretty explicit also.

1 Like

Exactly @ianharrigan.

I’m after the behavior that the trace function exhibits, of being able to inherently call .toString();

It would, for example, work if I write +"" but that’s also ugly.

IMHO an implicit conversion would be against the goal of a typesafe language design. So the way to go is to use an abstract - then you can have all the conversions you want/need, but still typesafe.

2 Likes

How about define a Helper myTrace (or whatever)
myTrace( any type of data you like )
have the Helper call trace( Std.string( any type of data you like ) )

You hide the complexity/ugliness within myTrace
Also gives you a handy place to set a Breakpoint in myTrace if it does not work as you want.

1 Like

@AdrianV: I said “define an implicit conversion”, meaning that, like for an abstract, I would define it, so it would be type-safe. But I’m curious if that can be done with a class. An abstract is not a class. From what I understood so far (which is admittedly not much since I’ve been using Haxe for just a couple of months), abstracts are just types with some inline code.

My situation is that I have a class, which has quite a bit of functionality in it, and I want to define implicit toString handling similar to the trace method. I’m still unclear about how the trace method does what it does. Why is that not considered “unsafe”? :slight_smile:. To be clear, of course I’m all for type safety! I wouldn’t be using Haxe otherwise :smiley:

@RMax Yes, sure, that could work, but is still one additional layer of functionality that I’d expect to happen automatically, particularly as a concatenation with a string makes it happen.

Compared to the hidden overhead of the actual output that trace enables:
1 small layer of encapsulation will be difficult to measure.

Recent experience with the 4GL Interpreter gives an Insight:
I had a small loop that would increment a counter and then show it, quite similar to trace( ).
I thought the singing and dancing through the Data, Operator and Noun stacks had significant overhead. Time spent doing the output itself was about 50x the Time running the Interpreter. I approached the updating now by only showing about 5 times a second instead of every time. After all the numbers would still be a blur to us Humans.

So I don’t see a Performance problem.
As far as Purity (perceived Technical Debt), I suggest a Pragmatic approach here and just go with it and worry about the likely other much larger Dragons you will need to slay later :slight_smile:

1 Like

@Kyliathy Implicit casts are only supported for abstracts (similar to operator overloading).

There’s quite a bit of magic around trace() / toString() / Std.string(), such as rest arguments for trace (the only other cases where Haxe allows this without Reflection is for externs and macros):

trace("hello", "world"); // hello,world

As you can see here, trace uses Dynamic under the hood. In the end it mostly calls Std.string(), whose implementation depends on the platform. On JS for instance, it’s far from pretty. In other cases like Java, it’s good enough to rely on the runtime toString() handling that the target already has.

@Gama11 Would you have an advice on how to combine abstract with a class in order to achieve the desired functionality? What I mean is that I have a class that does a bunch of stuff but can also represent itself as a String via toString. How could I use abstract in such a situation?

In .Net if I remember correctly everything is an Object and one of the core functions of Object is toString, therefore you can override toString pretty much everywhere and you have exactly what I’m looking for.

I’m stubborn in the way that I really want to avoid calling toString for objects that implement the method.

@RMax I agree and the pragmatic approach is what I’ve done. I’m pursuing this topic simply as a student of Haxe discussing with masters of this language :slight_smile:

What is the 4GL interpreter? Are you talking about Haxe or some other 4GL programming language?

@Kyliathy Well, you could have an abstract that wraps your class and forwards its fields, and use that type instead everywhere, something like:

@:forward
abstract ActualType(ImplClass) {
    public function new()
        this = new ImplClass();

    @:to function toString()
        return this.toString();
}

private class ImplClass {
    ...
}
1 Like

Interesting lesson! :slight_smile:

I assume you declared the class as private to force users to use the abstract.

By “forwards its fields” you mean returning new ImplClass();, right? That should handle all the forwarding, since it returns ImplClass.

Haxe is a very respectable 3GL with some Functional support.

My skewed Hobby project is a prototype (now poorly) supporting a 4GL approach..
2 Main benefits now are not being limited to English & Export to Haxe to further learn about programming languages.
4GL Interpreter reference is from my Hobby. The biggest problem of 4GL approach now (my opinion) is that all you can point to are proprietary ($) and no related Open Source community with them. Or if I found an Open Source 4GL project, it did not go in direction I wished to go.
Will publish skewed Hobby to GitHub later this month.
Sorry for any misunderstanding.

Sometimes, you can get better answers if you clearly explain why you want something (“how do I do X?”), instead of already deciding how you want to go about it and then asking “how do I do X using Y?” (or even worse, “how to do Y?”). We can only guess how you are gonna use what you’re developing. I think it’s important to explain what exactly is your goal. Of course if you’re learning Haxe and playing around, or if it’s just a riddle to solve for fun, we can have a theoretical discussion in vacuum.

For example pushing new TestStringPush() into an Array<String> - why do you want to do it? What is the real world usage for this? Depending on that, the following example may or may not satisfy you: Try Haxe !

It uses an Abstract on the “receiving side”, and you can pass any argument in without having to create an abstract for every class you want.

1 Like

That admonition is so fundamental that it even has its own WikiPedia page: XY Problem.”:+1:

@sundialservices and @gene-pavlovsky, you are pushing me in a direction that even 2 months ago would have resulted in a passive-defensive reaction (move on, ignore the thread because my question was answered, learn very little from all this).

But because this is a smaller community, and perhaps also because I’m more willing to admit my faults, here’s an honest admission of why I sometimes willingly avoid explaining the real problem.

Precisely in the case of the question I asked in this thread, the reasoning for not explaining everything is a mix of all the below (I’m just admitting my faults in the hope of becoming better in the future. And sometimes being public about it helps).

  1. I am afraid that my explanation will be misunderstood, resulting in either:
    a. wasting somebody’s (and my) time as they come up with a solution that doesn’t work.
    b. making me feel bad about it
  2. I am afraid that my problem will get a way simpler solution making me look like an idiot.
  3. I am afraid that my problem will get an answer that involves a heavy refactor, something that I am in no mood to do.
  4. I tend to think that my way is the right way and just answer the damn question so I may proceed and knock my own head against something hard which is sometimes the best way to learn.
  5. I am lazy to explain the problem especially when it’s something large.
  6. I assume that nobody will really try to understand my problem.

And yes, as I typed the list above, I have come up with the “Duuuuh!” answer for each of those points and I am now fully aware that it is at least silly if not even insulting to not act differently in the future.

I almost said that the only exception is when the problem really is very complex. But even so, I have often found that through the effort of “dumbing down” the problem in order to be posted somewhere has led me to realizations that solved the question before even posting it :slight_smile:.

That being said, there is also sometimes the “copyright factor”, a concept I actually dislike, but I somehow still sometimes have the silliness of thinking that if I post some stuff from my project somebody might steal my idea. That’s a very old part of me, but still somehow strong. The new me, however, wants to work for society, nor for oneself. Which is why this post even exists :wink:

Damn did this feel good to write! :smiley:

We all learn in different ways, it’s good to see you’re introspecting the different ways you learn yourself. Did you manage to solve the problem in the end in a way that makes you happy?

1 Like