I made some preparations here.
I was looking at MAP OVER KEEP FROM MAP OVER, and was surprised by how little different it looks inside from what just composing the HOFs would do. I was expecting something more like
with a (different) helper function for each of the three non-base cases (including the final ELSE clause, which would call a helper function that does what your code shown here does). The helper functions would be recursive; this toplevel one wouldn't be. My idea is that it'll be rare to use both of the MAP slots, so it's worth optimizing the other cases. What do you think?
Also, I propose to change the name of TERMINATE STREAM to STREAM _, TERMINATED ONCE... so it doesn't sound so imperative. Maybe even STREAM _, BUT TERMINATED ONCE...
Also, why does FIND FIRST have this (kludgy) limiting condition, but not KEEP? And why isn't FIND FIRST next to KEEP in the palette?
Thanks.
Agreed - done!
I'd rather not change this. The other blocks whose labels commence with "stream" are generators-from-scratch, this one is a mutator of an input stream, and all other mutators' labels commence with what they do (map, keep, combine - I just didn’t know a suitable verb for “making unique” - “deduplicate”? - besides Snap! uses the same word with lists).
If you actually feel “terminate” sounds too violent by Californian standards (“The Terminator”): how about “copy stream until” or “pass on stream until”?
The answers are actually related. Find first
doesn't report a stream, so for me it's comparable not with keep-stream
but with item-stream
and list items of stream
.
When a user specifies to limit the search to 10 items we actually want the search to stop immediately after the 10th attempt, right? See e.g. the code of list items of stream
: it has a comparable though different test.
BTW I added stream pattern
, inspired on what I think Haskell’s cycle
does. If you don’t like it, or think it’s too much, please feel free to leave it out.
Just curious, you don't have to change it or anything, but why don't the two new helper functions have gears in front of their names? And why did FUNCTION A become FUNCTION Q? You using a French keyboard? :~)
If it's really okay with you, I'd like COPY STREAM UNTIL.
This doesn't really answer why the same reasoning doesn't apply to KEEP. (Not that I want that! I raise it as an argument against the limit in FIND FIRST.)
The block is fine, but I'd call it STREAM REPEATING. "Pattern" seems to me to promise more generality than this delivers.
Also, why do you need a helper function? Why not just
?
Also, it suddenly occurs to me: Since this is a v10 library, we can specify the default number of input slots for variadic inputs to custom blocks! This one should default to one slot, and so should STREAM, GENERATE STREAM, and MAP. Do you agree?
And I want to relabel MAP as MAP _ OVER STREAM(S) _ to highlight its variadicness. Okay?
I’m used to prefix helper function labels with a gear. I feel the gear is a nice icon symbolizing hard-working parts “under the hood”. Function Q (as combined with function B) is an inside joke.
Fine with me. I think there's at least one help text (of keep-stream
) referring to it; changed.
I think I had that at first, but changed my mind. Stream repeating
is fine with me; changed.
A helper function is not absolutely necessary, but avoids repeatedly testing for a condition that won't ever occur.
I didn't check, but guess I do; changed.
Really that should have been the label in version 1 already. The reason I haven't changed it yet is I wanted to keep existing block names for upward compatibility. However we could define a hidden block with the old name, referring to the new block; done.
Right, but you didn't, this time!
Oh, Q, B, I get it.
That was also for a reason (though debatable): they are actually regular blocks and could have been in the palette, except it’s already very crowded.
I made the changes as indicated in post #145.
Besides (while doing so) I happened to notice the compatibility block show stream
was missing; I repaired that - both show stream
and map-stream
(without “s”) are now hidden blocks, referred to from their replacing blocks’ edit windows (making sure they won’t get lost again).
Minor changes:
- improved the definition of
list-stream
(replacingshow stream
) by using the so-called “kludgy” base case test fromfind first-stream
: it now handles a query for 0 items as I think it should:immediately reporting an empty list), - promoted
stream repeating
by 1 position within the palette.
just a question what is this library about
(is is like a data streaming system so you can share data across the cloud)
No. It's the SICP definition of Streams, i.e. lazy lists that can store huge or infinite amounts of data.
how is that possible
computers don’t have infinite ram
That's the trick. It doesn't store it. It stores a λ to generate it.
what does
mean
Ah. It Snap!, λ is .
What
sorry if I’m missing something
I don’t know many computer terms
I just learned python last year and only really did snap before that
Let me try to explain it. In the library is a block: .
It’s the equivalent of the imaginary list: .
The issue with the list is that, like you said …
… so it would be impossible to have such list. But then, for any practical purposes, you won’t ever need to count to infinity - it’s just that you may not know in advance what’s going to be the biggest number you’ll need.
This is where a stream, or lazy list, comes in. The aforementioned stream has only two items (initially):
- the first item, in this case: 7;
- a recipe (more usually called: promise) to calculate each following item. In this case, the promise says: next item = current item + 1. So item (2) of the stream is 7 + 1 = 8. Item (3) is 8 + 1 = 9. And so on.
Tnis is a trivial example, of course. Calculating items of some other sequences may be very complicated and time-consuming, so you don’t want to do it if they may not (all) be required after all. With streams, items are calculated as-needed. That being said, the streams mechanisms in themselves are much slower than those for lists, so working with streams is not always the best approach.
What I am going to use streams for is to create a Prolog-like interpreter in Snap! (Prolog is a language for e.g. puzzle solving, mathematical reasoning, AI, etc.). Some branches of a query may turn out to go on forever; using streams will allow searching other (more fruitful) branches in parallel, still yielding results. While I was studying an example in SICP (a famous book on programming - “best one ever written” , according to Snap!-cofounder bh), I gave the Streams library an overhaul - I expect it ‘s going to be distributed with Snap! v.10.
A new record?
Ugh we're moving in the wrong direction, uglifying additional blocks instead of de-uglifying FIND FIRST.
I'm not sure how doable this is, but really the way to make us both happy (protect against looping without bruising the abstraction) would be a Control block
which, I guess, would work by setting a hidden global variable that TAIL OF STREAM would decrement and test (only when actually cashing in a promise). Saving and restoring the count in case of nested limits.
Then those of us who prefer aesthetics over antibugging wouldn't have to know about your feature at all. And in return, you'd get to use it in KEEP. Or rather, around KEEP. :~)
Second thoughts:
Maybe it should have an ELSE clause for what to return if it hits the limit?
Make sure to restore the count when hitting the limit.
Maybe in case of nesting it should use the MIN of the old count and the new count when setting the inner count? Not sure.
Let’s stick with the facts. This is what the set of tests for base cases in the definition of the Streams library v1’s block show stream
looks like:
And this is my proposal for the equivalent (IMAO: improved) tests in v2’s list-stream
and find first-stream
:
find first-stream
actually reports “”.
The 3 notable differences are:
- Practical: the (always terminating) test for
number = 0
now precedes the (possibly not terminating) test for an empty stream. - Formal: the test for an empty stream is now independent of the underlying data structure (which has been abstracted away by the new
is stream empty?
block). - Aesthetical: both tests are now combined in a single
if
block.
Which of these changes is pushing your buttons?
As for your proposed wrapper block: I don’t see how that would work to prevent find first-stream
from getting stuck in an infinite loop … unless you rebuild the latter so as to report after each failed micro-step, or if you use metaprogramming. So: not doable, I think. But I may have missed something (I’m not the Pope, ex cathedra, or something).
My concern is not with the code inside the blocks, but with the way they look to users. It's the difference between
(beautiful) and
(not).
But now I'm re-reading what you wrote, and I see you're talking about LIST ITEMS a/k/a SHOW-STREAM, which has to be uglified that way; it's its entire point. Maybe way back in the conversation I confused you about what I called kludgy. And then you confused me by citing "kludgy" about something else. :~)
About how to implement it, lemme see if I can show you...
I'm laying a fiver on the table that you two don't agree on the new library before Snap!Con2024
My first reaction was "oh, no, we're practically there!" but I suppose agreement on one thing could unearth disagreement on something else.
PS In our i18n world you have to specify five of what unit. :~)