GithubHelp home page GithubHelp logo

Comments (14)

vrurg avatar vrurg commented on September 15, 2024 2

Well, it can:

You missed the point. The actual iterator logic is in map lexical scope, actually. :)

from problem-solving.

vrurg avatar vrurg commented on September 15, 2024 1

Just an example:

sub transform($v) {
    next if $v.contains("2");
    "<$v>"
}
say (^20).map(&transform);

Even the .map({...}) form is a good demonstration because the code block just cannot be in the same lexical scope where the iteration logic is located.

from problem-solving.

2colours avatar 2colours commented on September 15, 2024 1

A different way to phase this issue would be "next, last, and redo should have lexical scope rather than dynamic scope"

I don't think this reflects the Block vs Sub distinction. It's super useful and also fairly reasonable that a Block is transparent to all control structures. It could even look up all variables dynamically - I can imagine why that might be a bad idea but it could also make some sense. However, to have a subroutine that can be safely used as an independent unit of execution, is a different story.

it's very clear that gather is supposed to have dynamic scope, and next/last/redo "feel" similar enough to gather that I'm inclined to think that keeping their behavior consistent is the better design.

It seems to me that both statements can be challenged. I do think that take could also be taken as a cunning implicit callback and be subject to evalutation whether it's really worth it to let it slip through subroutines. However, let me point out that take does indeed have more of a callback interface that next, last and the likes don't have, and maybe for the better:

use v6.*; # To enforce next, last with return values
gather for (1..10) { .take } # This is going to work
(1..10).map: { .next } # This won't - has to use `next $_`
(1..10).map: { .last } # This also won't - has to use `last $_`

Now, of course those control statements could be adopted to also have the OO-looking interface - which is also a modification that might interfere with existing code, and then we can still ask if it's really expressive or just confusing.

from problem-solving.

2colours avatar 2colours commented on September 15, 2024

My proposal would be to keep control exceptions like the ones next and last create, inside Subs, hence throwing a similar exception to a legitimately missing loop to iterate.

If somebody prefers the former (perhaps unspecified?) behavior:

  • they can specify a CONTROL phaser to pass it through anyway
  • I don't know how you feel about pragmas but there could be one to set the behavior for one Sub or the whole CompUnit, for example

The topic of performance also came up from @lizmat . My stance on that is (besides the general sentiment of "it's better to be right slowly than wrong quickly"): the implementation doesn't need to be fast immediately. Loads of control structure optimizations will be available with RakuAST - in fact, most of the time, we could say it at compile time if an invalid next is used in a subroutine, by traversing from the usage of next towards the root Sub. At the very least, we can omit special handling when it's clear that there are no such control statements, or they are clearly scoped in a loop inside the Sub.

from problem-solving.

cfa avatar cfa commented on September 15, 2024

The synopses actually do mention the dynamic nature of a bare next (as opposed to a labelled next, for example) - however, this doesn't appear anywhere in the corresponding file of the specification.

See https://github.com/Raku/roast/blob/master/S04-statements/last.t#L31-L39 for last.

from problem-solving.

2colours avatar 2colours commented on September 15, 2024

The synopses actually do mention the dynamic nature of a bare next (as opposed to a labelled next, for example) - however, this doesn't appear anywhere in the corresponding file of the specification.

See https://github.com/Raku/roast/blob/master/S04-statements/last.t#L31-L39 for last.

I think this is the right place for design decisions anyway - so then the amendment woud be: introduce this in a new version, and modify the corresponding test.

from problem-solving.

codesections avatar codesections commented on September 15, 2024

Hmm, my (tentative) thought is that the current behavior is reflects the correct design. A different way to phase this issue would be "next, last, and redo should have lexical scope rather than dynamic scope". But, phrased like that, I'm slightly inclined to disagree – it's very clear that gather is supposed to have dynamic scope, and next/last/redo "feel" similar enough to gather that I'm inclined to think that keeping their behavior consistent is the better design.

On the other hand, lexical scope is more familiar to most programmers and I don't have strong feelings on the matter. So I'm certainly open to persuasion.

from problem-solving.

codesections avatar codesections commented on September 15, 2024

Even the .map({...}) form is a good demonstration because the code block just cannot be in the same lexical scope where the iteration logic is located.

Well, it can:

say (^20).map({
    next if .contains("2");
    "<$_>"
});

But, more generally, I agree that there are times when a dynamic scope to next is useful/clearer.

from problem-solving.

2colours avatar 2colours commented on September 15, 2024

Even the .map({...}) form is a good demonstration because the code block just cannot be in the same lexical scope where the iteration logic is located.

The .map({...}) form uses a Block, not a Sub. I'm not arguing against this behavior for a Block but a Sub. And consequently, that example looks like a good illustration of what I find confusing without any benefits.

from problem-solving.

codesections avatar codesections commented on September 15, 2024

I'm not arguing against this behavior for a Block but a Sub.

IMO having this behavior differ between Blocks and Subs would be highly confusing.

from problem-solving.

vrurg avatar vrurg commented on September 15, 2024

@2colours:

The .map({...}) form uses a Block, not a Sub. I'm not arguing against this behavior for a Block but a Sub. And consequently, that example looks like a good illustration of what I find confusing without any benefits.

Are you sure about no benefits? I consider the transform sub from my example to be very beneficial as means of code clarification and organization. .map(&transform) (or .map: &transform) seems to be way more self-explanatory than using a block with all the same logic transform provides. Basically, the routine should've be named transform-and-filter for the full clarity of it.

@codesections:

IMO having this behavior differ between Blocks and Subs would be highly confusing.

Not exactly. Think of return which is routine-scoped, so to say. The other point is that its semantics greatly differs from that of next and its "kins".

from problem-solving.

patrickbkr avatar patrickbkr commented on September 15, 2024

Just to throw in another code example that could potentially confuse me:

for @lists -> $x {
    $x.map({ process($_); next if !$_ }
}

from problem-solving.

2colours avatar 2colours commented on September 15, 2024

IMO having this behavior differ between Blocks and Subs would be highly confusing.

Well, what can I say, I think this is the whole point Blocks exist in the first place - to represent a piece of code with a scope that can be abstracted away while still providing inlining semantics. I can't see why this would be confusing - rather than Sub sometimes mimicking inlined behavior, by implicitly passing control flow back to the caller...

from problem-solving.

2colours avatar 2colours commented on September 15, 2024

The other point is that its semantics greatly differs from that of next and its "kins".

Well, does it? Are they not all pragmatisms over structural code, sort of jumps that are at least guaranteed to be tied to certain control structures? I would say return and next are much more alike than take and next - I wonder if take is considered a control statement to begin with, it's not obvious at least. By semantics, it's much more like a callback.

Also, food for thought: linked part of synopsis also says For instance, next with a label will prefer to exit a loop lexotically - and this is present in the current workings as well. next BAR isn't going to fall through anything but a bare next will - does this inconsistency seem as good as it did back when this language was going to be the new Perl?
Also, if one does want the fallback behavior for a subroutine, it could still be made explicit, right? I have mentioned the idea of a pragma but at the end of the day, I wouldn't oppose something like next * either. The asterisk even plays nice with the symbolics of dynamic behavior.

.map(&transform) (or .map: &transform) seems to be way more self-explanatory than using a block with all the same logic transform provides

I'm afraid this is a "let's agree to disagree" situation - that seems to me like a misuse of a subroutine. To me, it breaks the expectations of a subroutine - the expectation that a successfully executed subroutine won't interfere with the code of the caller - after all, that's part of the reason why I abstracted it away with a subroutine and not a Block, right?
It can actually lead to weird thoughts: what return value does that subroutine even have? After all, next is not a callback, and indeed it terminates the control flow of the subroutine in the first place. Indeed, it behaves like an exception, without looking like one.

Just to throw in another code example that could potentially confuse me:

for @lists -> $x {
    $x.map({ process($_); next if !$_ }
}

@patrickbkr Could you please elaborate? Because here the for loop seems superfluous. So far, I haven't seen anybody questioning that map˙implies a loop, therefore all possible uses would hook up there, if anywhere.

from problem-solving.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.