[FWIW, dev work on this was started 7 yrs ago and ended 4 years ago.]
I like many features of this language -- although the feature I don't like is traits/mixins (one of the lang's central tenants), which seems like a bad idea in the general case -- it makes implementation inheritance even more abusable, rather than preferring delegation.
"hello" print
in addition to
someObj.ToString()
in Java-esque languages are just horrible, IMHO.
An arbitrary object should have absolutely no business knowing how to render itself, nor should a string know how to do I/O on itself, as these operations depend on the context of their usage.
Rather, for example, if you want to stick with message-sends:
console [print "foo"]
would delegate the printing of "foo" to the console object.
(here the brackets reify the message -- not sure about the syntax in Potion.
I can't tell you how many time in my code or in others', a .ToString() was expected to do one thing (pretty-print, for example) when instead it used some default method, such as the base method that prints " <a Foo>" or something. These things even end up in production output.
Context: I write C++ at work. I also worked on compile-to-js language in the past.
> An arbitrary object should have absolutely no business knowing how to render itself
What should, in your opinion?
I find it straightforward to implement even plural serialize-to-string operations within classes or at least the same module as the class. One example that immediately comes to mind is a quaternion that can print itself as either 4 numbers (raw) or 3 numbers (euler angles). That said in C++ you don't have a standard `ToString`; you do have a standard shift-to-ostream that serves the same purpose. Rust also has Display and Debug traits you can implement, which I guess makes more sense to me than a single `ToString` as it resolves a common ambiguity.
Anything further than string/debug-print, though, I think your objection makes more sense, as "render" (as in "to HTML" and such) is more cross-concerns; in these cases I usually opt for "function in the same package as the class, but a separate source file".
> nor should a string know how to do I/O on itself
But isn't this a different issue compared to the previous case? The representation of "a string" is public knowledge while that of "an arbitrary object" is not.
> ... a quaternion that can print itself as either 4 numbers (raw) or 3 numbers (euler angles).
Then at last you presumably have overloads that are descriptive, such as .ToRawString() and .ToEulerString(), correct? If you did a cout<<q, which one are you going to get?
Having a non-abstract base .ToString() defined in Object (and that requires no format parameters) causes the issues.
Of course you could define an enum and take it as a format, like .ToString(Format.Euler), but even if you define well-named overloads, the other .ToString() is still hanging there and probably doesn't do anything useful.
So, for most cases in most current language, my statement was probably a bit strong and should be weakened to "the framework should not assume there is one best/default way to render any Object to a string regardless of context and purpose and offer a default implementation at the top of the hierarchy".
Ultimately, however, I think the stronger argument does hold, but is not easily addressed in most languages, or at least is not addressed in any consistent to concise way across many languages.
> the framework should not assume there is one best/default way
Best, probably not.
Default, I'd argue that Rust's approach of defining Display and Debug traits (interfaces) in the stdlib is reasonable. It is explicit, specific, and unambiguous. While having a trait system helps a lot, it's not hard to imagine instead of a single `ToString()` defined by Object, it has `ToDebugString()`, `ToDisplayString()`, etc..
that does not really matter as long as it is consistent. If I'm doing cout << q I'm certainly just debugging or writing to some log, am most certainly in a hurry and just want to see if I'm going to get (NaN, NaN, NaN) or not.
> An arbitrary object should have absolutely no business knowing how to render itself,
Well, `toStrig` and its inverse make sense mostly for value types and these usually have ONE canonical representation. SQL has been based on converting values to/from strings since... always. E.g., when I want to insert a GUID into a table in SQLServer, I send it in as a... string. JDBC driver returns it as a string as well.
Problems with string conversions arise when a value type doesn't have a single canonical representation, e.g., dates and times.
dev work didn't stopped 4 years ago, it's just not main priority anymore, and I'm blocked on two major things. (see below)
console "foo" print
would be the potion syntax, if console would exist as IO object. just compare to smalltalk, forth or ruby with smalltalk syntax.
print is not used as serialization method, and not used as pprint.
pprint would be the proper pretty-printing extension over print,
and dump is the default serializer.
I don't get the problem with "hello" print. It's not comparable to someObj.ToString().
The potion equivalent would rather be hello string vs hello dump vs hello pprint.
Sorry if I misunderstand -- Wouldn't this send "foo" to the console object (which it probably wouldn't understand), and then send "print" to whatever that returned, which isn't what you want? You don't want to send "print" to "foo" first and then send that to the console either. You want to send the message [print "foo"] to the console object - you are delegating the action.
[Using ST syntax] If you wanted to do it w/o a reified message, you could do:
console print "foo"
Where "print" is really "get-default-printer-object" which uses something like doesNotUnderstand to respond to "foo" as a message, or
console print: "foo"
As a keyword send, or to combine the approaches somewhat:
(console stdio-printer) print: "foo".
I will RTFM again so I can get a better handle on the Potion syntax - unlike ST which I'm more used to, it's not as clear to me yet at first glance what is a msg and what is an arg.
Objects knowing how to render themselves is great for debugging. In fact, many of us see toString just as a debugging function, and nothing meant for end-user presentation in non-debug console output.
I agree, generally. For that, I like Python's "repr" concept. A debug format should, in my opinion, match the definition syntax and be largely be handled by the language.
That's almost comically underspecified. One big concern that comes to mind, is the string a complete view of the object (and thus suitable for serialization) or not? Or is it more succinct and suitable for a print statement?
Yes, I get the Java stuff, I was being sarcastic because it totally missed the context it was built with, in favour of some generalist mainstream adoption critique which misses the point.
Have you seen the homepage?
The Potion Pledge "everything is an object" bit was a good hint.
Sure. What kind of string? For debugging? If so, is a recursive? For output to a log? For output to a widget in a UI? To a string suitable for a report? What encoding? What language?
Why not add an expectation, then, for what kind of string it is? You just have to have one. E.G. perl has .perl, which has the constraint that, for any obj, it should be that obj == (EVAL obj.perl). It doesn't matter what that is, as long as you agree on it. Having objects that know how to print themselves isn't intrinsically problematic.
That is serialization, which is one purpose for converting to a string. Dynamic languages already tend to be able to print objects in a way that's meaningful -- such as most REPL-based languages that have a reader (or eval) and a printer. As do ML-derived languages the print in constructor format, but they tend not to have readers or eval. Having it called .perl in this case is already a step in the right direction from toString, as you know what its for.
Not really abandoned. It's just not the main priority anymore. And perl6 decided to do something else, to go forward with a very shortsided and wrong approach (again).
There are two major blocks here,
1) the parser surviving a GC. There was one help from outside, but without real code. It almost got fixed by myself recently. macros depend on this, and improving the compiler and adding a matcher on macros.
2) threads support for the GC. hard problem. would need to switch to boehmgc, as fellow Tiny(Ruby,Python) folks did with this codebase. But they got not much farther. Only wren did. I liked his approach.
The TLS cert if unfortunately out of my hands. Someone else is maintaining the domain.
I plan to use potion, the VM and compiler for a perl fork, codename p2.
perl5 and perl6, in spirit of parrot, just 40x faster, and better than the ongoing perl on JVM plans.
See https://github.com/perl11/potion/blob/p2/README.p2
So naturally I'm going to enhance potion also.
New are libpotion.so, improved make times, seperated config and
dist/install targets via config.inc, dist.mak.
I didn't like that every make probed and shelled too much. Now it only does when needed.
I fixed load readline (the wrong path),
and changed the LOADER_PATH to (lib, PREFIX/lib/potion, .)
. at first is a security risc.
added support native extensions: darwin .dylib vs .bundle.
With load .pnb is favored over .pn.
I also added libdisasm support for nicer jit debugging, and improved the parser interface for multiple language support.
I'll add in the next months dlcall (FFI), exception handling, arbitrary precision math and a lot of libraries to support perl5 and nqp, the perl6 bootstrapper.
I'll probably come up with a fast intermediate language to write those libraries, and I'm not sure if it will be potion (the language) or a small fast subset of perl.
Rather, for example, if you want to stick with message-sends:
would delegate the printing of "foo" to the console object. (here the brackets reify the message -- not sure about the syntax in Potion.I can't tell you how many time in my code or in others', a .ToString() was expected to do one thing (pretty-print, for example) when instead it used some default method, such as the base method that prints " <a Foo>" or something. These things even end up in production output.