Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.perl should be replaced with a pluggable system #91

Open
ajs opened this issue Aug 19, 2019 · 8 comments

Comments

@ajs
Copy link

commented Aug 19, 2019

.perl should be replaced by a pluggable infrastructure for converting to structured strings so that converting to v6.e is just as reasonable as converting to json or v5.

@ajs

This comment has been minimized.

Copy link
Author

commented Aug 19, 2019

From my post to reddit. This suggestion is flawed because it gets stomped on by classes that override Str and because the proposed syntax (~<format>) conflicts with some existing syntaxes.


Recent discussion has sparked a re-evaluation of the .perl method, and I'd like to bring up some thoughts about what it does, what it should do and perhaps why .perl wasn't as good a name as we always thought.

Whether we call .perl .code or .repr or .whatevertheheck, one of the problems that Perl 6 has is that the method namespace has become a battleground. It's essentially the Perl 6 keyword namespace, or at least where we deal with the conflicts that most languages deal with at the keyword level.

What I would really like is to not use another basic word slot, but rather improve something that we already use.... and in thinking about that, I started to reflect on what else we want out of a conversion between an object and a structured string.

That's when it hit me! We already have a way to convert to strings, and Perl 6 really loves meta-operators! (technically what I'm about to suggest isn't a meta-operator but a parameterized operator)

So, here's the proposal:

  1. Deprecate .perl. No real reason to remove it, but it should be phased out. For now it's just an alias for .Str(:as<v6>)
  2. .Str gets a named parameter: :as. e.g. .Str(:as<json>) or .Str(:as<nqp>) or .Str(:as<v6.e>) or even .Str(:as<v5>).
  3. Objects that want to override their stringification in a specific format can provide a as-<format> method.
  4. For default conversion of a new format, the Str::As namespace is used, so Str::As::json is where your object-to-JSON plugin goes (in reality, your module, say JSON::Converter::Nifty probably has an export flag that asks it to install a Str::As::json so that you can choose from multiple implementations).
  5. ~ by default does not pass :as, but ~<...> does. So ~<json> $foo is $foo as JSON.
  6. ~<vx.y> $foo is $foo in a form that is expected to be comprehensible to the vx.y grammar.

Note: ~< is already an infix and ~<foo> already means stringify the autoquoted list <foo> ... this might not be a problem as in the former case, we might be able to resolve ambiguity and in the latter case requiring a space for the older meaning has precedent elsewhere, but perhaps something else works as well?

The Str method on Any should probably look something like this (pseudocode):

    method Str(Any:D: :$as) {
        if not $as.defined or not $as { self.basic-stringification }
        elsif self.^can("as-$as") { self."as-$as"() }
        elsif $as.substr(0,1) eq 'v' { self.as-version-compat($as) }
        else {
           require ::("Str::As::$as");
           "Str::As::$as::stringify"(self);
        }
    }

So now providing a stringification for some new format (e.g. YAML) is as simple as writing a module in the Str::As namespace for it that defines as stringify subroutine (e.g. Str::As::yaml::stringify).

@lizmat

This comment has been minimized.

Copy link
Member

commented Aug 19, 2019

FWIW, for the .perl (or whatever it will be called) I can see use in it being required to return a subtype of Str, simply to indicate that there's code inside (similar to ObjAt and ValueObjAt). This could then be used for dispatch, e.g. not complaining when trying to EVAL it.

For other things such as providing a YAML or JSON representation, I think a similar approach could be taken. But I'd still favour just providing a .yaml or .json method for those, which perhaps should also return a dedicated subclass of Str then.

@FCO

This comment has been minimized.

Copy link
Member

commented Aug 19, 2019

Would that be possible to get the code from routines? I mean say sub { 42 }.code print sub { 42 }

@lizmat

This comment has been minimized.

Copy link
Member

commented Aug 19, 2019

Not by default, I don't think. Afaik, the actual source code doesn't exist in the byte code, unless you have something like use trace active. Of course, something could be devised, and could be useful. Perhaps the only thing that would be needed would be a file offset and number of bytes to be kept (and know where the original source file lives, of course).

@FCO

This comment has been minimized.

Copy link
Member

commented Aug 19, 2019

I was needing that to make Red CLI automatically write migration code files from existing models

@ajs

This comment has been minimized.

Copy link
Author

commented Aug 19, 2019

@FCO great question. The goal, here is just to do what .perl does today, but with a more flexible output format and better naming. .perl does not currently do this, but you could definitely rig the compiler to provide the source as an annotation on Code objects (presumably optionally, since people don't want the extra bloat in the routine case). You can't just reverse-engineer the VM instructions because they may have been optimized to the point of unrecognizability.

@niner

This comment has been minimized.

Copy link

commented Aug 20, 2019

The Str method could simply become a multi with a named as argument. The implicit *%_ means that if an object does not support the target serialization format, it will fall back to basic stringification which could work in many cases like plain numbers.

@ajs

This comment has been minimized.

Copy link
Author

commented Aug 20, 2019

Looking over the code, it seems that Str is already a multi, so my original idea could work, and speaking of things that could work...

$ perl6 -e 'sub prefix:<< ~<v6> >>(\thing) { thing.perl }; say ~<v6>(1,2,3,a=>1)'
(1, 2, 3, :a(1))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.