8

This code works:

(3,6...66).contains( 9|21 ).say  # OUTPUT: «any(True, True)␤»

And returns a Junction. It's also tested, but not documented. The problem is I can't find its implementation anywhere. The Str code, which is also called from Cool, never returns a Junction (it does not take a Junction, either). There are no other methods contain in source. Since it's autothreaded, it's probably specially defined somewhere. I have no idea where, though. Any help?

jjmerelo
  • 22,578
  • 8
  • 40
  • 86

1 Answers1

9

TL;DR Junction autothreading is handled by a single central mechanism. I have a go at explaining it below.

(The body of your question starts with you falling into a trap, one I think you documented a year or two back. It seems pretty irrelevant to what you're really asking but I cover that too.)

How junctions get handled

Where is contains( Junction) defined? ... The problem is I can't find [the Junctional] implementation anywhere. ... Since it's autothreaded, it's probably specially defined somewhere.

Yes. There's a generic mechanism that automatically applies autothreading to all P6 routines (methods, operators etc.) that don't have signatures that explicitly control what happens with Junction arguments.

Only a tiny handful of built in routines have these explicit Junction handling signatures -- print is perhaps the most notable. The same is true of user defined routines.

.contains does not have any special handling. So it is handled automatically by the generic mechanism.

Perhaps the section The magic of Junctions of my answer to an earlier SO Filtering elements matching two regexes will be helpful as a high level description of the low level details that follow below. Just substitute your 9|21 for the foo & bar in that SO, and your .contains for the grep, and it hopefully makes sense.

Spelunking the code

I'll focus on methods. Other routines are handled in a similar fashion.

method AUTOTHREAD does the work for full P6 methods.

This is setup in this code that sets up handling for both nqp and full P6 code.

The above linked P6 setup code in turn calls setup_junction_fallback.

When a method call occurs in a user's program, it involves calling find_method (modulo cache hits as explained in the comment above that code; note that the use of the word "fallback" in that comment is about a cache miss -- which is technically unrelated to the other fallback mechanisms evident in this code we're spelunking thru).

The bit of code near the end of this find_method handles (non-cache-miss) fallbacks.

Which arrives at find_method_fallback which starts off with the actual junction handling stuff.

A trap

This code works:

(3,6...66).contains( 9|21 ).say  # OUTPUT: «any(True, True)␤»

It "works" to the degree this does too:

(3,6...66).contains( 2 | '9 1' ).say  # OUTPUT: «any(True, True)␤»

See Lists become strings, so beware .contains() and/or discussion of the underlying issues such as pmichaud's comment.

Routines like print, put, infix ~, and .contains are string routines. That means they coerce their arguments to Str. By default the .Str coercion of a listy value is its elements separated by spaces:

put 3,6...18;                    # 3 6 9 12 15 18
put (3,6...18).contains: '9 1';  # True

It's also tested

Presumably you mean the two tests with a *.contains argument passed to classify:

my $m := @l.classify: *.contains: any 'a'..'f';
my $s := classify *.contains( any 'a'..'f'), @l;

Routines like classify are list routines. While some list routines do a single operation on their list argument/invocant, eg push, most of them, including classify, iterate over their list doing something with/to each element within the list.

Given a sequence invocant/argument, classify will iterate it and pass each element to the test, in this case a *.contains.

The latter will then coerce individual elements to Str. This is a fundamental difference compared to your example which coerces a sequence to Str in one go.

raiph
  • 31,607
  • 3
  • 62
  • 111
  • We should probably document more explicitly that Junction magic. That's probably the reason why Junctions are not in Any. Thanks a lot. – jjmerelo Apr 04 '19 at 10:57
  • 1
    Hi JJ. I've just inverted the focus of my answer to the junction mechanism. It's just a first quick cut. I've got to do a bunch of other stuff for most of today and tomorrow, but if you give me a few days I hope to more clearly understand the high level details of the code I spelunked and update this answer. That might be helpful towards documenting the mechanism. I should note that I consider the junction mechanism to be a well designed -- it's not "magic" in the negative sense folk often mean in a technical context. So I'll be eliminating use of that word. :) – raiph Apr 04 '19 at 11:21
  • you've probably realized this opens a whole new Pandora box of undocumented stuff... – jjmerelo Apr 04 '19 at 17:23
  • 2
    I just got back online. I haven't had time to realize anything. :) The doc says "Autothreading happens when a junction is bound to a parameter ... that doesn't accept values of type `Junction`. Instead of producing an error, the signature binding is repeated for each value of the junction." To me that's essentially it. The rest is mostly unpacking this mechanism's simplicity, generality, and power -- I currently see the doc situation more like the lack of a menu in a Forrest Gumpian box of chocolates than a Pandora's box. :) – raiph Apr 04 '19 at 18:42
  • that document says that. But if all routines, or most, have a fallback autothreading option for Junctions, well, it should be at least noted. – jjmerelo Apr 05 '19 at 05:33
  • 1
    For me, it was one of those "Aha Erlebnisse" some year ago. It is visible through `dd Junction.^mro; # (Junction, Mu)`. The fact that a `Junction` is **not** an `Any`, allows autothreading to work. This is why we need to be careful with creating candidates that have `Mu` types in their signature: either they will have to handle `Junction` themselves, or they have to rely on `Junction`s being handled by calls that these candidates make. If anything needs to be documented, I think it is this fact, rather than documenting `Junction` behaviour everywhere. – Elizabeth Mattijsen Apr 05 '19 at 10:00