This channel is intended for people just starting with the Raku Programming Language (raku.org). Logs are available at irclogs.raku.org/raku-beginner/live.html Set by lizmat on 8 June 2022. |
|||
Kaiepi | i find it kinda odd because a typecheck on `my` can happen at the container level, but `our` installs a lexical symbol like `my` w/ an extra binding to `OUR` so you can kinda just | 00:01 | |
Nemokosch | no idea why `ok` vanished but this one works even in the REPL | 00:05 | |
also as `raku -e` | |||
01:15
deoac joined,
Kaipei left
01:20
razetime joined
01:26
deoac left
03:41
Kaipei joined
03:58
razetime left
04:43
Heptite left
07:58
razetime joined
10:35
Kaipei left
11:25
m_athias left,
m_athias joined
12:20
Kaipei joined
|
|||
<@511826957558611968> dev.to/lizmat/dont-fear-the-grepper-6-4i about laziness, caching and all | 12:52 | ||
12:55
habere-et-disper joined
12:59
razetime left
13:00
habere-et-disper left
|
|||
zacque | Thanks, that helps! The difference in assigning `Seq` object to a positional or scalar variable is new to me 🤯 | 13:07 | |
And how the `map` and `grep` can be chained together *lazily* | 13:09 | ||
Nemokosch | A seq object can be assigned to a scalar variable | 13:11 | |
and bound to a positional variable | |||
think of that as a different dimension | |||
zacque | Just trying it out a little bit, when the Range object is defined to infinity, i.e. `(0..*)`, assigning the `map` result to a positional or scalar is the same | ||
Yup, I can appreciate that | 13:12 | ||
Nemokosch | can you write the code? | ||
zacque | Something like: ```raku | 13:13 | |
my @myseq = (1..*).map({ say "calculating $_"; $_ + 2}); | |||
@myseq.say; | |||
@myseq[0].say; | |||
@myseq[0..10].say; | |||
``` | |||
which is different from `my @myseq2 = (1..10).map({ say "calculating $_"; $_ + 2});`, I meant | 13:14 | ||
So, for a fixed size, assigning to a positional variable is eager by default, but assigning to a scalar variable makes it lazy; for infinite size, both cases are lazy by default? | 13:15 | ||
Nemokosch | uh, moment | 13:20 | |
what you are saying sounds reasonable for me but I wouldn't know tbh | 13:23 | ||
m: my @myseq = (1..*).map({ say "calculating $_"; $_ + 2}); say @myseq[].WHAT; | 13:24 | ||
because it claims to be an array, I would assume the STORE method handles it in a special way | 13:26 | ||
somehow it knows that the Seq is infinite, I don't know | |||
13:28
discord-raku-bot left,
discord-raku-bot joined
|
|||
Kaiepi | that's all backed by docs.raku.org/type/Iterator#method...until-lazy in this case | 13:30 | |
Nemokosch | ohh gotcha | 13:32 | |
it's not that the infinite version knows that it's infinite | |||
it's that the finite version doesn't consider itself lazy... | |||
m: say (1..10).map({ say "calculating $_"; $_ + 2}).is-lazy | 13:33 | ||
mind: blown | |||
docs.raku.org/type/Iterator#method_is-lazy | 13:34 | ||
Superstart033 | my $Mind = “blown” | ||
Nemokosch | so something that might be lazy, might not _consider itself_ lazy | ||
lol | |||
perhaps is-finite would have been a better name or idk | 13:36 | ||
or always-lazy | |||
zacque | This is confusing... 🤣 | ||
Nemokosch | technically it's simple | ||
"is-lazy" in fact means "is-always-lazy" | |||
since the infinite version "is always lazy", it won't get pushed | 13:37 | ||
while the other version is just potentially lazy, and therefore it can (and will) get pushed | |||
zacque | Hmm, that doesn't help | 13:39 | |
Nemokosch | in what regard? 😅 | ||
zacque | I think in the article, it says `my @result = (1..5).map({ say "calculating $_"; $_ + 2});` is roughly equivalent to ```raku | 13:40 | |
my @result; | |||
for (1..5).map({ say "calculating $_"; $_ + 2}) { | |||
@result.push($_); | |||
} | |||
``` | |||
But in reality, it's more like ```raku | |||
my @result; | |||
for (1..5).map({ say "calculating $_"; $_ + 2}) { | |||
@result.push-until-lazy($_); | |||
}```? | |||
That's why lazy Seq doesn't produce value | |||
Nemokosch | not exactly | ||
push-until-lazy pushes several elements | |||
therefore it replaces the whole "for" | 13:41 | ||
it's a lower level | |||
``` | 13:42 | ||
my @result; | |||
(1..5).map({ say "calculating $_"; $_ + 2}).push-until-lazy(@result); | |||
``` | |||
I think you could try this | |||
I will, too | |||
oops, needs .iterator | |||
``` | |||
my @result; | |||
(1..5).map({ say "calculating $_"; $_ + 2}).iterator.push-until-lazy(@result); | |||
``` | |||
fixed, second attempt | |||
<@210313526928080896> do you think this can be further "for-ified", or the low-level Iterator interface would be needed to expand this? | 13:45 | ||
zacque | Hmm, but how about `(1..*).map({ say "calculating $_"; $_ + 2}).iterator.push-until-lazy(@result);` | 13:46 | |
Nemokosch | yes, same call | ||
zacque | I'm trying to see how `push-until-lazy` fits into this picture | ||
Oh? | |||
Nemokosch | > The Iterator role implements this method as a no-op if is-lazy returns a True value, or as a synonym of push-all if not. | ||
so in the second case, it does nothing | |||
in the first case, it redirects to .push-all | |||
zacque | Ohh, that tells the difference between `my @myseq = (0..*).map({ say "calculating $_"; $_ + 2});` and `my @myseq = (0..10).map({ say "calculating $_"; $_ + 2});` | 13:48 | |
Nemokosch | yes... it's all because of the different value for .is-lazy | ||
which is a somewhat misleading name | |||
that's why I say you could think of it as is-always-lazy | 13:49 | ||
zacque | Is it? The name seems good to me | ||
Nemokosch | docs.raku.org/type/Iterator#method_is-lazy | ||
I mean... `(0..10).map({ say "calculating $_"; $_ + 2})` this is "lazy" | |||
it doesn't calculate anything | 13:50 | ||
see? | |||
doesn't calculate anything, yet says `False` | |||
zacque | Huh? Ok, that's surprising | ||
Nemokosch | I think the reason is that iterators are dumb in general | 13:51 | |
they don't know much about the underlying content | |||
if they had to figure it out, we would surely lose lazy evaluation | |||
so when in doubt, they rather lie in a humble way, in order to stay _actually_ lazy | 13:52 | ||
🤣 | |||
zacque | Hmmm, wait, I'm confused again | ||
Nemokosch | tenor.com/view/no-nooo-nooooo-cryi...f-14628566 | ||
zacque | Then how do you explain the difference in behaviour if both are lazy? | ||
Haha | 13:53 | ||
Oh nooo | |||
Nemokosch | they _are_ both lazy | ||
but they _don't both consider themselves_ lazy | |||
zacque | ??? | ||
Nemokosch | only one of them actually knows about itself that it's lazy | ||
zacque | Hmmm... maybe it has something to do with the assignment method? | ||
Nemokosch | depends on what "it" is | 13:54 | |
zacque | Assignment method forces evaluation? | ||
Nemokosch | what I said applies regardless the assignment method | 13:55 | |
zacque | I'm confuse about this statement | ||
Nemokosch | why? | ||
zacque | Oh... | ||
Nemokosch | imagine yourself in the case of a poor iterator | 13:57 | |
it can only retrieve one value at once | |||
how can it know if it belongs to lazy data or not? | 13:58 | ||
basically only if it's told so | 13:59 | ||
now, when an infinite range is involved, it will be told so | |||
apparently, if the _lazy_ prefix is used, it will again be told so | |||
> my @result = lazy (1..5).map({ say "calculating $_"; $_ + 2}); | 14:01 | ||
zacque | I see | ||
Just now I was also surprised by the fact that `(1..10).map({ say "calculating $_"; $_ + 2}).is-lazy` doesn't print out "calculating ..." | |||
Nemokosch | m: my @result = (.map({ say "calculating $_"; $_ + 2}) andthen .is-lazy.say && $_); dd @result; | ||
oops, almost | |||
m: my @result = lazy (1..5).map({ say "calculating $"; $ + 2}) andthen .is-lazy.say && $_; dd @result; | 14:03 | ||
I'm curious about this | |||
zacque | It doesn't print out anything... | ||
Nemokosch | so annoying | ||
m: my @result = lazy (1..5).map({ say "calculating $_"; $ + 2}) andthen .is-lazy.say && $_; dd @result; | |||
watch out | 14:04 | ||
yes! that's it | |||
`my @result = lazy (1..5).map({ say "calculating $_"; $ + 2}) andthen .is-lazy.say && $_; dd @result;` | 14:05 | ||
no, that's not it... | |||
run it for yourself pls | |||
it says `False` | 14:06 | ||
and then it proceeds to talk about "lazy list" | |||
yes! | 14:07 | ||
zacque | Ya, false, also, same error for `dd @result` | ||
Nemokosch | so `(1..5).map({ say "calculating $_"; $ + 2})` still considers itself not lazy | ||
despite not calculating anything | |||
zacque | Oh, the `False` is from evaluating `.is-lazy.say`! | ||
Nemokosch | yes | ||
zacque | Ok, but this is true | 14:09 | |
The conclusion is that the `lazy` effect from the `lazy` keyword comes after assignment? | |||
Nemokosch | that's part of the conclusion | ||
zacque | Hold on, I need to digest it | 14:11 | |
Nemokosch | don't assume that I understand all of it 😄 | 14:21 | |
I think I understand "enough for now", enough to integrate the code into a kind of logic | |||
14:24
discord-raku-bot left,
discord-raku-bot joined
|
|||
zacque | Ahhh, so to understand why `say (0..10).map({ say "calculating $_"; $_ + 2}).is-lazy;` doesn't print any "calculating ..." line, you do `my @result = lazy (1..10).map({ say "calculating $_"; $ + 2}) andthen .is-lazy.say && $_;`, which prints `False` | 14:30 | |
14:30
razetime joined
|
|||
It shows that the `Seq` object is sometimes lazy even if `.is-lazy` returns `False` | 14:36 | ||
So, there is a contradiction. QED. Ha! | 14:37 | ||
Nemokosch | 🤣 | 14:38 | |
I think the `lazy` prefix "decorates" the value some way | |||
so that it becomes aware of its laziness | |||
without that, it doesn't know about it | |||
zacque | But maybe method call is lazy? That's why `(0..10).map({ say "calculating $_"; $_ + 2}).is-lazy.say;` doesn't print any "Calculating ..." line | 14:39 | |
So your theory is not quite right? | 14:40 | ||
Nemokosch | with this "black box approach", we can only rely on Occam's razor | ||
the only thing I feel needs "hacking" in my theory is the `lazy` prefix that can somehow let iterables know that they are lazy | |||
on the other hand, I don't think method calls are any special, nor that we need to postulate that | 14:41 | ||
map returns a Seq, without fetching any values | 14:42 | ||
in my terms, that sounds enough to call it "lazy" | |||
in the article of lizmat, the terminology is the same | 14:44 | ||
zacque | It seems to me that it only knows after assignment (or maybe after creating the object and "decorating" its value) | ||
So I think assignment does play an important role here | |||
Nemokosch | well, let's remove `lazy` and try that way | ||
zacque | I see | 14:45 | |
Nemokosch | try $result and @result as well | 14:46 | |
they will both state `False` but only @result will trigger eager evaluation | 14:47 | ||
_that_ is indeed due to the list assignment | |||
but for the scalar, it will still be lazy and still lie about it | 14:48 | ||
zacque | Huh? You meant comparing these two? | ||
Nemokosch | yes | ||
Rog | Skimming through this conversation, I would say make sure you’re not observing things that aren’t actually there by using the REPL | ||
Because the REPL will cause stuff to evaluate | |||
Nemokosch | I don't think any of this was caused by the REPL | 14:49 | |
zacque | Both are `False` | ||
Nemokosch | yes | 14:50 | |
zacque | Ah, I see, I'm not aware of that, thanks! | ||
Nemokosch | and how are they located compared to the other say's | ||
zacque | It's by invoking `raku` | ||
Nemokosch | oh okay | ||
add `dd $result` and `dd @result` | |||
after the respective assignments | |||
and remove `lazy` | 14:51 | ||
I mean, I thought you wanted to investigate the role the assignment plays | 14:52 | ||
zacque | Ah, yes I am | ||
Huh? | |||
Nemokosch | with lazy, I think it's almost identical; that's what we have checked, right? | 14:53 | |
zacque | Ah, yes | 14:54 | |
My mind is a mess now haha | |||
Nemokosch | I'm trying to "inline" the important points about the process | ||
zacque | With separator | 14:57 | |
Nemokosch | 1. assignments: $ wraps the right handside directly, @ calls STORE (which under the hood calls push-until-lazy - source: Kaiepi :D) | ||
zacque | ? | ||
Nemokosch | why | ||
zacque | What why? | ||
Nemokosch | the question mark why | ||
zacque | Oh, I mean is this the "answer"? Haha | 14:59 | |
Nemokosch | meant to continue this | ||
2. `lazy` - it's a statement prefix; might even be weird usage here, I'm not sure what it exactly does here. docs.raku.org/syntax/lazy | 15:01 | ||
it seems to do "the right thing", whatever that may be exactly | |||
it seems to me that it wraps the right handside into a new Iterable (a Seq in our case) that does know about its laziness | 15:03 | ||
zacque | For lazy vs eager comparison | ||
Nemokosch | docs.raku.org/routine/lazy gotcha | 15:05 | |
see your last line of output? | |||
it's explicitly marked as lazy | |||
zacque | For comparison until infinity | 15:08 | |
Not sure why it computes until 101 | |||
Yup | 15:09 | ||
Nemokosch | both? | ||
zacque | Nope, only for the scalar one | ||
Nemokosch | what did the array version produce? | 15:11 | |
zacque | A lazy list, I commented out the `dd @result` because error `Cannot .elems a lazy list onto a Array` | 15:12 | |
Nemokosch | I think this is solely related to the presentation of different lazy structures | 15:13 | |
by the way "Cannot .elems a lazy list onto a Array" sounds so ungrammatical to me | |||
do you have any idea how this should be read? xD | |||
zacque | Think so | ||
Haha! I simply ignore the "onto a Array" part | 15:15 | ||
Nemokosch | so 101 might be an arbitrary number used for gisting | ||
have you seen bisectable6 (the bisect bot) before? | |||
maybe we could check when this phrasing was introduced | |||
xD | |||
zacque | Yup, it limits to first 100 | ||
Haha, you can git blame on that | 15:16 | ||
Nope | 15:17 | ||
Nemokosch | okay, wait for it... | ||
if I knew where it was in the code 😉 | |||
15:17
Nemokosch joined
|
|||
only works on the main IRC chat <#633753286209699870> | 15:18 | ||
zacque | github.com/rakudo/rakudo/blob/14fb....pm6#L2534 | 15:19 | |
Nemokosch | gist.github.com/8f8c79094f8124f178...79b3715647 | 15:20 | |
zacque | Oh, interesting | 15:21 | |
Looks like a bug to me because the `.message` method is not define | 15:23 | ||
Nemokosch | github.com/rakudo/rakudo/commit/71...aecc4e9c8c welp | ||
the .what value changed | |||
I think it was invented later | 15:24 | ||
15:24
Nemokosch left
|
|||
the caption is a bug though, pretty sure | 15:25 | ||
I think the intention was like "cannot push onto an array" and stuff like that | |||
and `.elems` is miscategorized somehow | 15:26 | ||
well we pretty much caught a bug | 15:30 | ||
minor bug but still a bug 😄 | |||
I would say this could be our 5 minutes of fame | 15:40 | ||
but it's not obvious how it should be fixed xD | |||
because the refactor created overlaps in the calls | |||
stuff that used to look different looks identical now | |||
that's why the output changed | |||
zacque | Yeahhhhh, we caught a minor bug haha! | 15:47 | |
I see | 15:48 | ||
I'll leave it to the devs lol | |||
Gtg, bye | |||
Nemokosch | hahaha | 15:49 | |
byez | |||
lizmat | FWIW, I explicitly did not want to mention "lazy" in the grepper blog posts | 15:54 | |
not until the blog posts about iterators | |||
Nemokosch | fair enough | 15:59 | |
lizmat: any idea about how the exception message could/should be fixed? | |||
lizmat | eh, which one ? | 16:00 | |
16:02
razetime left,
razetime joined
|
|||
Nemokosch | github.com/rakudo/rakudo/issues/5093 | 16:09 | |
17:12
razetime left
17:31
Kaipei left
18:19
habere-et-disper joined
|
|||
habere-et-disper | `flat` suggests that it flattens things, but an empty array is flattened into an empty sequence which in turn cannot be flattened. Please can someone illuminate on this Chesterton Fence? | 18:27 | |
m: ().flat | |||
camelia | ( no output ) | ||
habere-et-disper | If I turn instead to `slip`, then an empty sequence is still not discarded unless I use the prefix form: | 18:28 | |
m: ().Slip | |||
camelia | ( no output ) | ||
habere-et-disper | m: |() | ||
camelia | ( no output ) | ||
habere-et-disper | m: dd ().flat; dd ().Slip; dd |() | 18:29 | |
camelia | ().Seq slip() block <unit>() |
||
19:02
Kaipei joined
|
|||
Nemokosch | Could you provide an example where this behavior has unwanted consequences? | 19:22 | |
An empty list is flat by itself, I think so far so good | 19:23 | ||
habere-et-disper | I'm grokking the Exercism Flatten Array exercise | 19:24 | |
Yes, I'm with you on the empty list being flat itself. | 19:25 | ||
19:49
Heptite joined
|
|||
habere-et-disper | Here's my confusion... I don't think I understand what a container is in raku: | 20:08 | |
m: m: my $empty-scalar = []; my @empty-array = []; say $empty-scalar.WHAT; say @empty-array.WHAT; say (1, 2, $empty-scalar).flat; say (1, 2, @empty-array).flat; | |||
camelia | (Array) (Array) (1 2 []) (1 2) |
||
habere-et-disper | Why does the empty array get flattened, but the empty scalar (which holds an empty array) not? Are scalars containers and arrays not? | 20:09 | |
20:44
Kaipei left
|
|||
Nemokosch | habere-et-disper: fair enough, it is indeed a bit... complicated | 21:07 | |
how to put it | 21:11 | ||
first, let's clarify that I think this is documented behavior | |||
> Interprets the invocant as a list, flattens non-containerized Iterables into a flat list, and returns that list. Keep in mind Map and Hash types are Iterable and so will be flattened into lists of pairs. | 21:12 | ||
docs.raku.org/routine/flat | 21:13 | ||
you might argue that the phrasing is a bit inaccurate because Array IS a container type as well, not only data type | |||
by the way, you didn't retrieve what I think you wanted to retrieve | |||
m: my $empty-scalar = []; my @empty-array = []; say $empty-scalar.VAR.WHAT; say @empty-array.VAR.WHAT; say (1, 2, $empty-scalar).flat; say (1, 2, @empty-array).flat; | |||
now this is the real WHAT 🤣 | 21:14 | ||
m: my $empty-scalar = []; my @empty-array = []; say $empty-scalar.VAR.WHAT; say @empty-array.VAR.WHAT; say (1, 2, $empty-scalar).flat; say (1, 2, @empty-array).flat; | |||
habere-et-disper | m: my $empty-scalar = []; my @empty-array = []; say $empty-scalar.VAR.WHAT; say @empty-array.VAR.WHAT; say (1, 2, $empty-scalar).flat; say (1, 2, @empty-array).flat; | ||
camelia | (Scalar) (Array) (1 2 []) (1 2) |
21:15 | |
Nemokosch | what is going on... | ||
yeah thanks | |||
Scalar is a simple thing; it's what I would just call "variable" in any other language I know | 21:16 | ||
you can wrap "anything" up into a Scalar | 21:17 | ||
habere-et-disper | So a super-type for lack of a better word. | 21:18 | |
Nemokosch | it's a type that provides interface for mutation via assignments, and gives an entity to the underlying data | 21:19 | |
an Array is not an entity in the sense a Scalar is | |||
you cannot decontainerize it further | 21:20 | ||
it's structurally not "one thing" | |||
it provides "an interface for mutation via assignments" but that interface is inherently fragmented | 21:21 | ||
an Array is kind of like a vector/grid of Scalars | |||
the individual elements can be decontainerized from the Scalars - but not the Array itself | 21:22 | ||
habere-et-disper | So the Array is an immutable structure? | 21:25 | |
21:26
discord-raku-bot left
21:27
discord-raku-bot joined
21:31
discord-raku-bot left,
discord-raku-bot joined
|
|||
habere-et-disper | Thank you for the long explanation! :-) | 21:33 | |
I think I might hide until the hyperwhatever makes an appearance: | |||
m: say [1, 2, [3]].flat; say [1, 2, [3]].[**]; | |||
camelia | (1 2 [3]) HyperWhatever in array index not yet implemented. Sorry. in block <unit> at <tmp> line 1 |
||
Nemokosch | 😂 | 21:35 | |
by the way, is there a reason you have to use arrays in particular? | |||
as opposed to lists | 21:36 | ||
habere-et-disper | No -- just trying to solve the set exercise whose tests have them. | 21:37 | |
Nemokosch | then I'd say just use lists - they are not a container type, and they do not wrap the items into scalars | 21:39 | |
21:39
Kaipei joined
|
|||
habere-et-disper | Thanks! Helpful. :-) | 21:45 | |
23:02
habere-et-disper left
|