Raku / problem-solving

🦋 Problem Solving, a repo for handling problems that require review, deliberation and possibly debate
Artistic License 2.0
70 stars 16 forks source link

Hash slices should have an option to return a hash #432

Open alabamenhu opened 4 months ago

alabamenhu commented 4 months ago

Short description

Hash slices should have an option to return a hash.

More details

Given the code

sub foo(%h) { 
    say %h;
}

my %bar = <a 1 b 2 c 3 d 4>;
foo   %bar<a b>:p;      # error
foo   %bar<a b>:kv;     # error 
foo %(%bar<a b>:p);     # works but ugly
foo   %bar<a b>:kv.hash # works but ugly

There is no simple way to call foo with a subset of %bar, other taking the list and generating a hash out of it.

alabamenhu commented 4 months ago

I would propose adding a :h-for-hash adverb. While this can be fairly easily added through a module with the addition of the following operator multi:

multi sub postcircumfix:<{ }> (Associative \base, Positional \keys, :$h!, *%other) {
    die "Unsupported combination of adverbs ({keys.join: ' '}) passed to slice on {base.VAR.name}" 
        if any %other<kv v p k>:exists;
    %(samewith base, keys, :p, |%other)
}

sub foo(%h) { 
    say %h;
}

my %bar = <a 1 b 2 c 3 d 4>;
foo %h<a b>:h; # success

This feels like it should be more core. This is possible in Raku's sister predecessor by retaining the % sigil (where $bar<a> → 1, @bar<a b> → [1, 2], %bar<a b> → {a => 1, b => 2}), but I don't see any discussion about one way or the other during the design phase

raiph commented 4 months ago

Hi @alabamenhu

I added a heart, but not a thumbs up, because I love where you're going with this but had one thing I'm not sure about: the rationale for wanting to push it to core, and, on top of that, for pushing it straight to core.

My view of Raku has "always" been (ie from around 2000) that as much as possible should revolve around the real core, not move into that core itself.

I've bitten my tongue about this for a couple decades because things weren't really ready to make that approach compelling, but with the ecosystem and RakuAST where they are today it feels like we're finally within a couple years of that becoming a PL differentiator worth striving for.

So, while we're not there yet, it nevertheless now feels pressing to ask: what is it about this sweet approach you describe that feels like it should go core before first living in the ecosystem for a few years?

alabamenhu commented 4 months ago

Absolutely agree @raiph (and I did, after all, provide the code to do that ^_^ )

The reason to me it feels core is it it's lost functionality from the 5->Raku transition which IMHO was probably overlooked rather than decided against and blocks something that intuitively feels like it should be possible. I admit, "feels like core" is a highly subjective term, but in saying that, I'd hope that my fairly extensive work in core-adjacent modules of pretty diverse nature at least gives me a bit of perspective. To me, this is akin to realizing we don't have a particular formatter type for sprintf that's common in other languages and adding it, but of course, 'tis but one view among many perfectly valid ones.

I'm not averse to being convinced otherwise, though.

gfldex commented 4 months ago

There more I think about this, the more I feel we got the default wrong. Please consider:

my @a = 1..42;
@a[5,6,7][1,2].say; # (7 8)
my %h = 'a'..'e' Z=> 1..5;
%h<b c d><b c>.say; # Type List does not support associative indexing.

I believe we got away with this because my %foo = %h<b c d> just works, because it's a list of Pairs. The "just works" part is quite DWIMy but not very consistent. The whole thing breaks down when we get to foo(%h), as @alabamenhu discovered. There may be a bigger issue at play with a few devils hiding in the details.

niner commented 4 months ago

I disagree. I expect the hash slice to be in the order of the keys I supplied. Returning an unordered hash would defy this expectation.

gfldex commented 4 months ago

When the :kv adverb is supplied, I would expect a list of Pairs in the order of request too.

lizmat commented 4 months ago

@gfldex you mean: keys and values alternating in the order of the request?

alabamenhu commented 4 months ago

There more I think about this, the more I feel we got the default wrong. Please consider:

my @a = 1..42;
@a[5,6,7][1,2].say; # (7 8)
my %h = 'a'..'e' Z=> 1..5;
%h<b c d><b c>.say; # Type List does not support associative indexing.

I believe we got away with this because my %foo = %h<b c d> just works, because it's a list of Pairs. The "just works" part is quite DWIMy but not very consistent. The whole thing breaks down when we get to foo(%h), as @alabamenhu discovered. There may be a bigger issue at play with a few devils hiding in the details.

Note that the my %foo = %h<b c d> doesn't work due to an odd number of number of elements. A potential gotcha lies in doing

my %foo = %h<b c>;
say %foo; # {2 => 3}

Unlike in 5, we can't pass associative context into &circumfix:<{ }> to toggle on pairs. Either :kv or :p is needed. Neither work with the binding operator, though.

alabamenhu commented 4 months ago

Did not mean to close it, errant mouse click.

gfldex commented 4 months ago

@gfldex you mean: keys and values alternating in the order of the request?

Indeed. There is List.pairs and Any.pairs to get the current output. The adverb :pairs feels missing as a shortcut on [] and {} subscripts.

gfldex commented 4 months ago

I spend some time on thinking about the arguments presented in this discussion. Quite frankly, what I expect doesn't matter all the much. What matters is, what Raku expects. Or to prase it differently, do we value tradition higher then composebility? This further lead to the question, if stringent composebility in a programming language leads to a (strangly) consistent language?

It appears to me that we are lacking guidelines when it comes to language design. And that is important, if we want Raku to become the 100 year language. If so, it needs to be able to change. What presses the question: "In what direction?". In my eyes that feels like a task for the steering council, reviewed by the community.

lizmat commented 4 months ago

@gfldex: %h<b c d> is not a list of pairs, my %foo = %h<b c d>:p is.

FWIW, I've missed a :vk adverb and .vk method more. And I think %(%h<a b c>:p) is not that ugly at all, but very clear and expressive.

gfldex commented 4 months ago

@lizmat :p is quite helpful. What is bugging me, is that we treat Array quite DWIMy, while we don't really do the same for Hash. Please consider the followign example:

    sub bar(*@a) {
        dd @a;
    }

    bar [1,2,3][0,1];

    sub foo(*%h) {
        dd %h;
    }
    foo |%(%(:1a, :2b)<a>:p);

I like the Array case because it composes very well. The Hash case requires extra hoops. I'm a human composer, so my capacity to keep the balls in the air is rather limited. Our brains utterly depend on similar things to look similar and different things look different to work well. In the end, an Array is an ordered list of Any() and a Hash is an ordered list of Pair (with lots of nice interfaces). But the handling can be quite different when it comes to binding. When I see something like that - a break in a chain of similarities - I ask my self: "How would I teach that to a novice?". I always hope for the answer to be: "Easily!". Is it, in this case?

Many years ago - I believe 1 ½ years before 6.c - the mood was quite gloomy in #perl6-dev. I reminded the audience that it took 10 years for Python to take off, with another 2 years for wider adoption. We are getting close to 10 years now. My blog has the same amount of hits per post then 8 years ago. In my eyes, the success of Python stems from the fact, that the Year-2000-bug woke a lot of politicians up. At least here in Germany, CS faculties suddenly where showered with money. First year students went from about 80 to 400. They needed to teach them programming and finding a good C-programmer is hard. Finding a good C-programmer that is also a good teacher, is almost impossible. Javascript didn't work back then outside of the browser, so Python won that race. Are we winning the race? I hope that better composability leads to simpler and more importently shorter docs. You have to be able to cram a language into 6 month of a few hours per week or it wont fly in higher education. No problem for quite a few languages, that are not Raku. That Raku is supposed to be your last language, not your first, is all good and well. But that may mean that we are in for the long haul. I don't know if that's what it takes to make a 100-year-language. I hope not so.

Sorry for the wall of text - hopefully not to much off topic. But lately, I found myself looking at alternatives. There are quite a few.

alabamenhu commented 4 months ago

I can understand the view that %foo<a b c> should return a hash, but to me it feels like it holds to a what goes in comes out principle. Pass a scalar, get a scalar, pass an array, get an array.

That's why I think the :h is the best alternative. To stay consistent, this could even apply to an array, perhaps, in a situation where we'd want that:


my @foo = <a b c d e>;

say @foo[1..3];    # (b c d)
say @foo[1..3]:kv; # (1 b 2 c 3 d)
say @foo[1..3]:h;  # %(1 => b, 2 => c, 3 => d)
lizmat commented 4 months ago

For the array case, you could argue that:

say @foo[1..3]:h;  # %(b => 1, c => 2, d => 3)

would make more sense.

alabamenhu commented 4 months ago

For the array case, you could argue that:

say @foo[1..3]:h;  # %(b => 1, c => 2, d => 3)

would make more sense.

I put it that way to stay stay consistent with the other positional slice modifiers, which treat the indices as keys. I can see the use though of a quick flipper, but I think adding in :ah and :ap (antihash, antipairs) might be a bit much lol

jubilatious1 commented 4 months ago

I'm okay with %bar<a b>:p.hash;.

I don't like the idea of adding a new :h adverb.

sub foo(%h) { 
    say %h;
}

my %bar = <a 1 b 2 c 3 d 4>;
#foo   %bar<a b>:p;      # error
#foo   %bar<a b>:kv;     # error 
foo %(%bar<a b>:p);     # works but is this really that bad?
foo %[%bar<a b>:p];     # works but is this really that bad?
say %bar<a b>:p.hash;     # works but is this really that bad?
foo   %bar<a b>:kv.hash; # works but is this really that bad?
foo   %bar<a b>:p.hash; # Bruce points this out.
foo   (%bar<a b>:p).hash; # Bruce points this out.
say   (%bar<a b>:p).hash; # Bruce points this out.
put   (%bar<a b>:p).hash; # Bruce points this out.

Results in:

{a => 1, b => 2}
{a => 1, b => 2}
{a => 1, b => 2}
{a => 1, b => 2}
{a => 1, b => 2}
{a => 1, b => 2}
{a => 1, b => 2}
a   1
b   2
ab5tract commented 2 months ago

I think :h is an elegant solution to the original problem highlighted here and for my taste it would find more use than a lot of the adverbs we've already put in place,

lizmat commented 2 months ago

Further question: should :h return a mutable Hash, or an immutable Map?

jubilatious1 commented 2 months ago

Further question: should :h return a mutable Hash, or an immutable Map?

:h and :m ?

We're entering the realm of obscure single-letter flags here, à la command line fu.

ab5tract commented 2 months ago

Further question: should :h return a mutable Hash, or an immutable Map?

:h and :m ?

We're entering the realm of obscure single-letter flags here, à la command line fu.

As with most single-letter CLI flags, I would personally expect these single-letter forms would be shorthands for longer variants.

Regardless of whether that assumption is true: Why draw the line at :p and refuse either :h or :m? It seems arbitrary to me.

jubilatious1 commented 2 months ago

Regardless of whether that assumption is true: Why draw the line at :p and refuse either :h or :m? It seems arbitrary to me.

"The magical number seven, plus or minus two: Some limits on our capacity for processing information." Miller, G. A. (1956). Psychological Review, 63(2), 81–97. https://doi.org/10.1037/h0043158

I hear Rakuuns talk about Huffman encoding, but I rarely hear Rakuuns talk about Miller's estimate of "item capacity limits in working memory." For a recent review on this topic, see:

"George Miller’s Magical Number of Immediate Memory in Retrospect: Observations on the Faltering Progression of Science" Cowan, N. (2015). Psychological Review, 122(3), 536–541. https://doi.org/10.1037/a0039035https://psycnet.apa.org/record/2015-10435-001 https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4486516/

Maybe you have a low opinion of Miller's work, but humans do have an "item capacity limits in working memory" which is a non-arbitrary number we should cautiously respect. Raku is at four (4) standard :colon-adverbs now, which is one less than Miller's lower limit of five (5):

:v
:k
:kv
:p
lizmat commented 2 months ago

Eh, and :delete and :exists??

jubilatious1 commented 2 months ago

Eh, and :delete and :exists??

So you're saying the limit should be nine (9)?

lizmat commented 2 months ago

I'm saying we're already past 5. For better or worse.

jubilatious1 commented 2 months ago

I'm saying we're already past 5. For better or worse.

For worse, according to Miller (1956).

ab5tract commented 2 months ago

The upper limit is 9, correct?

I'm not familiar with Miller's work but I get the sense from the wording in the title that he wasn't proposing a hard boundary. Certainly "seven plus or minus two" doesn't directly imply a preference for the lower bound over the higher one.

All that said, I don't find the reference particularly salient to the current discussion. We have core methods with far more than nine candidate signatures already, without involving adverbs.

Edit: I am not ot sure what you mean by "standard :colon-adverb", either. We use adverbs where they apply, the only thing "standard" about the list you present is that they apply to both Positional and Associative objects. Q has its own adverbs, as does rx, and so forth.

raiph commented 2 months ago

@jubilatious1

I'm with you on the general need for sensitivity about matters such as:

I'm considering returning to the above issues/questions in later comments.

But for tonight I only want to address what I understand to be the relevance of Miller's work (or really the modern scientific understanding of the same broad topics) to the discussion so far in this thread.

Raku is at four (4) standard :colon-adverbs now, which is one less than Miller's lower limit of five (5)

Miller's limit was about "immediate memory" -- remembering a tiny number of things for a few seconds as part of "executive" function (consciously thinking about things).

As far as I can tell, to the degree that immediate memory is involved in this context it's to do with fleeting local reasoning about code -- mentally evaluating / understanding individual fragments of a single expression or statement, or evaluating / understanding how those parts combine to form larger parts.

In such reasoning each adverb contributes, at most, just one unit of information to Miller's limit. The number of adverbs "available" in standard Raku isn't material, just the number that are actually used in a single fragment of code being understood at a given moment.

The expressions/statements in this issue thread so far have at most one adverb in them. Furthermore, one doesn't need to hold them in immediate memory for more than a split second, because as soon as you remember or find out that, say, :h denotes something like .Hash (or .Map or whatever), then you no longer need to keep :h in mind.

So I'm currently thinking that the adverb usages seen in this thread amount to zero, in terms of Miller's limit, for anyone that knows Raku, and 1 at most, for those who don't know Raku, or who have forgotten what a particular adverb means.