alex.chabatarevich
2022-2-23 09:31:25

@alex.chabatarevich has joined the channel


laurent.orseau
2022-2-23 11:14:00

Oh, right, thanks! I think I knew some of this at some point, but just forgot—spoiled by DrRacket.


christos.perivolaropo
2022-2-23 13:31:55

I have heard some tongue in cheek comments from racketers against CLs loop. I used to like loop a lot back when I used CL primarily. It could be a bit more extensible but an EDSL for loops is a good idea I think. Why do racket people tend to not like it?


ryanc
2022-2-23 13:44:20

The phase level is not part of the syntax, it’s determined by the context that the syntax is (or will be) used in. If you’re writing a macro, that’s usually (but not always) the same as (syntax-local-phase-level). If you’re writing code that analyzes a fully expanded module, then you need to keep track of the phase level yourself, increment it when you enter a define-syntaxes or begin-for-syntax form, etc.


massung
2022-2-23 14:03:49

I use loop because it’s in the spec and crazy powerful. But it isn’t exactly “lisp-y”. The mini DSL that can do a lot, but you need to be very knowledgeable about all the different keywords (and which ones are optional).


laurent.orseau
2022-2-23 14:12:25

you mean https://docs.racket-lang.org/reference/for.html#%28form._%28%28lib._racket%2Fprivate%2Fmore-scheme..rkt%29._do%29%29\|do loops? (I’ve never really used CL) I don’t think anyone has anything against them, but Racket’s for loops and for/fold are pretty powerful too, usually it’s all you need.

Though myself sometimes I do revert to using named lets such as (let loop ([i 0]) (displayln i) (when (< i 10) (loop (+ i 1))))


christos.perivolaropo
2022-2-23 14:34:04

the loop macro in cl is like a dsl for implementing loops, it can be used to map over multiple collections in different ways and collect the results in a data structure or fold or just run implerative code for every element. There is a racket version <https://planet.racket-lang.org/package-source/jphelps/loop.plt/1/0/planet-docs/loop/index.html|for the loop macro> but it doesn’t seem to be as central in scheme as it is in cl


christos.perivolaropo
2022-2-23 14:36:43

I guess it’s pretty lisp-y, my CL fanatic friends seem in love with it. One could argue that it’s not very functional and CL doesn’t try to be functional so maybe in that way ti makes sense that schemers haven’t really embraced it.


laurent.orseau
2022-2-23 14:38:54

There’s a nice comparison at the end of these docs. Is there anything in particular in loop that Racket’s for doesn’t do (well)?


massung
2022-2-23 14:43:24

I don’t really consider (loop for i from 1 to 10 do (print i)) Lisp-y compared to something like iterate: https://iterate.common-lisp.dev/.

But aside from some missing power, I generally like racket’s for more than both. If there was a missing “feature” of for in racket, it’d be a way of combining for/fold and for/list together.

For example, implementing something like scanl (https://hoogle.haskell.org/?hoogle=scanl) in Racket using for (read: iteratively) is frustrating and requires mutable variables. It can be done recursively easily enough, but then you run into the risks of blowing the stack, etc.


christos.perivolaropo
2022-2-23 14:47:41

scanl is much more useful in haskell because the mental model is “making stuff is (almost) free, accessing it is (unpredictably) expensive”. I don’t think I have ever used it in any other language.


christos.perivolaropo
2022-2-23 14:48:24

but you are right, it would be awkward to implement


laurent.orseau
2022-2-23 14:49:25

@massung The #:result keyword can help with this (for/fold ([n1 1] [n2 1] [l '()] #:result (reverse l)) ([i (in-range 10)]) (define n3 (+ n1 n2)) (values n2 n3 (cons n3 l))) '(2 3 5 8 13 21 34 55 89 144)


capfredf
2022-2-23 14:50:06

Got it. Thanks, @ryanc


christos.perivolaropo
2022-2-23 14:50:35

nice


massung
2022-2-23 14:58:28

No, not nice, because it requires reverse. :wink:


massung
2022-2-23 15:00:42

In CL I’d just use rplacd and build out the tail of the list as it grew in O(N) time. But, sadly, I don’t have that option in Racket. for/list will do that for me (it doesn’t need to reverse internally), but what it does isn’t available to me unless I try and force it using mpair? objects.


laurent.orseau
2022-2-23 15:02:30

for/list does reverse internally I believe


massung
2022-2-23 15:02:49

:face_vomiting:


laurent.orseau
2022-2-23 15:02:54

why?


massung
2022-2-23 15:03:03

Absolutely unnecessary extra work being done for no reason


laurent.orseau
2022-2-23 15:03:23

that’s just the way singly-linked list work, doesn’t it?


massung
2022-2-23 15:03:40

Sure, but you don’t need to implement building a list that way.


massung
2022-2-23 15:04:09

The resulting list is that, but you can build it by saving the head cons and then just mutably updating the tail as you go until finished. Then return the head cons.


laurent.orseau
2022-2-23 15:04:59

except that this uses mutable pairs, and this blocks many other things the compiler can do


massung
2022-2-23 15:07:27

Those are silly implementation details and someone limiting themselves for some silly “purity” reason that doesn’t hold water. It’s like me saying a C compiler shouldn’t be allowed to do X when working with an array of floats because the programmer said they were const. That’s silly. They’re all just bytes. The end-result to the programmer/user is const, but what the compiler does to make things efficient under-the-hood may completely ignore that (and should for the sake of efficiency and performance).


laurent.orseau
2022-2-23 15:10:17

Well, in our case it boils down to the very old debate of mutable vs immutable, and Racket has chosen to keep things immutable as much as possible. See for example: https://web.mit.edu/6.005/www/fa15/classes/09-immutability/


massung
2022-2-23 15:10:45

What you’re saying is that for/list is likely implemented in racket somewhere, and because of that, it’s limited to what Racket exposes to programmer. Okay. But then Racket really needs to expose the ability to build a list in O(N) time iteratively by just updating the tail.

The concept of pair? (immutable) vs. mpair? (mutable) is only conceptual and enforced at the implementation-level. Which means it can also be ignored. It’s not like mpair? objects are stored in RO memory with write violation exceptions.


laurent.orseau
2022-2-23 15:12:48

Nowadays you’ll have to get down to the Chez Scheme level I guess :wink:


massung
2022-2-23 15:15:29

Immutable vs. mutable is an end result and not something that the internals should care about.

The link you provided - with StringBuilder - is a bad example because the programmer has access to the StringBuilder object at runtime and do whatever they want with it (hence it’s mutable).

But, an exposed core function like call-with-output-string is not constantly calling append-string every time you write to the port provided to the function. It has (its own version of) StringBuilder inside. To you - at runtime - it’s some immutable object you can’t mess with. But internally it’s 100% some mutable thing that finally results in an immutable object.

List building should absolutely be done similarly in for/list and other constructs like build-list, make-list, etc.


massung
2022-2-23 15:17:01

The idea that it might not be blows my mind and befuddles me. How could anyone believe that to be acceptable? :wink:


laurent.orseau
2022-2-23 15:18:09

build-list is implemented using the stack, so without explicit reverse: (let recr ([j 0] [i n]) (cond [(zero? i) null] [else (cons (fcn j) (recr (add1 j) (sub1 i)))]))


massung
2022-2-23 15:19:02

That said, most of my career has been about pure performance and memory management: either at the assembly level (to the level of avoiding LHS stalls, cache misses, and instruction pipeline flushes) or very large data manip. where something like “reverse” would absolutely kill.


massung
2022-2-23 15:19:25

So I’m heavily biased, and I know it. :wink:


laurent.orseau
2022-2-23 15:20:07

reverse is still O(N) in any case, as long as you don’t do it at every iteration. So what if immutability allows for some other optimization that make it up for a call to reverse?


massung
2022-2-23 15:20:10

> build-list is implemented using the stack Yeah, that’s just as bad.


laurent.orseau
2022-2-23 15:21:03

> > build-list is implemented using the stack > Yeah, that’s just as bad. Do you know how the stack is implement though? Maybe it uses mutable cons cells :stuck_out_tongue:


massung
2022-2-23 15:21:45

> reverse is still O(N) Complexity-wise it’s O(N), but the implementation doesn’t allow that, because it’s most likely implemented using recursion as well instead of being iterative. This means your limitation is the stack and not memory.


massung
2022-2-23 15:21:58

> Maybe it uses mutable cons cells Okay, now you’re just poking the bear! :stuck_out_tongue:


christos.perivolaropo
2022-2-23 15:22:13

I like to think that internally a list might be a merkle tree


christos.perivolaropo
2022-2-23 15:23:17

to take my mind off the possibility of mutation


christos.perivolaropo
2022-2-23 15:23:56

it was a big wakeup call to me that it’s not possible to change a mutable hash to an immutable one in O(1)


laurent.orseau
2022-2-23 15:24:20

Implementation of reverse: (letrec-values ([(loop) (lambda (a l) (if (null? l) a (loop (cons (car l) a) (cdr l))))]) (loop null l))


christos.perivolaropo
2022-2-23 15:24:21

that I might be thinking of things in a different way than the compiler assumes


christos.perivolaropo
2022-2-23 15:28:35

Not really, I have been using for for a while now, and I haven’t had a lot of frustration other than the nesting becoming a bit too deep in some cases. I wasn’t implying that loop is better in any technical way, I was trying to get opinions on it.


laurent.orseau
2022-2-23 15:30:29

@massung > Complexity-wise it’s O(N), but the _implementation_ doesn’t allow that, because it’s most likely implemented using recursion as well instead of being iterative. This means your limitation is the stack and not memory. Not with tail-call optimization no. The implementation above is basically a named let: (define (reverse l) (let loop ([a null] [l l]) (if (null? l) a (loop (cons (car l) a) (cdr l))))) which is tail-recursive. So it’s really O(N)


massung
2022-2-23 15:31:36

Some refreshingly good news for me in this discussion. Still for/list (or any list accumulator) using it… yuck.


laurent.orseau
2022-2-23 15:33:04

In some cases you can save the reverse because you have a follow up operations that also reverses. Like if you map then filter then in principle you can save the 2 reverse (although there’s filter-map of course)


laurent.orseau
2022-2-23 15:34:18

I often use a (custom) rev-append which appends left to right in reverse order to save time, in particular because often left is the reverse of a list I’m processing.


massung
2022-2-23 15:35:05

That it’s even something you need to think about implementing is a sign of a problem. My 2p.


laurent.orseau
2022-2-23 15:35:53

There are lots of things I need to implement :stuck_out_tongue: I have lots of problems. Please give me lots of 2p


massung
2022-2-23 15:36:24

What we’re talking about here are such fundamental operations in Lisp, the most optimal implementation should just be the default for the internals of the VM. You - the programmer - should never be thinking you have to make up for it.


laurent.orseau
2022-2-23 15:37:22

Well, it could well be the case that I’m breaking some compiler optimizations by doing this actually…


laurent.orseau
2022-2-23 15:38:43

Interesting comment in the implementation of filter: ;; accumulating the result and reversing it is currently slightly ;; faster than a plain loop (let loop ([l list] [result null]) (if (null? l) (reverse result) (loop (cdr l) (if (f (car l)) (cons (car l) result) result))))


laurent.orseau
2022-2-23 15:41:00

(I’m wondering why build-list doesn’t do the same thing though)


massung
2022-2-23 15:42:52

> accumulating the result and reversing it is currently slightly Total guess: poor performance testing. Likely true for small lists and large #s of iterations, but breaks down when dealing with lists with millions (or billions) of items.


massung
2022-2-23 15:43:44

Esp. if GC ended up becoming a problem needing to be dealt with w/ all the cons creations and freeing taking place dropping one list and making a whole new one in its place.


laurent.orseau
2022-2-23 15:45:22

I would very much trust the GC on lists. It’s smarter than me. Re reverse, because you can use tail-call opt. you don’t use the stack, so I would guess that acc+reverse is actually the only viable solution for very large lists.


massung
2022-2-23 15:46:26

+1 on reverse using TCO. But GC is not smarter than you. You can actually avoid creating garbage in the first place. :wink:


laurent.orseau
2022-2-23 15:46:48

i’m pretty (edit: somewhat :slightly_smiling_face: ) sure it will avoid garbage altogether in this case


massung
2022-2-23 15:48:25

I hope you’re right :wink:


laurent.orseau
2022-2-23 16:23:52

if you’re curious, and good with assembly code, you can take a look at the generated code from the reverse above. Just save #lang racket/base (define (reverse l) (let loop ([a null] [l l]) (if (null? l) a (loop (cons (car l) a) (cdr l))))) into reverse1.rkt, and run: PLT_LINKLET_SHOW_ASSEMBLY=1 racket reverse1.rkt If you spot some inefficiencies I supposed discussing them with mflatt could be fruitful!


soegaard2
2022-2-23 16:25:20

This discussion reminded me of Olin Shivers papir on implementing loop in Scheme. https://www.youtube.com/watch?v=PCzNwWmQdb0

If you need a macro challenge: use the algorithm in the paper to implement a CL-style loop in Racket.


samth
2022-2-23 16:32:13

Two things to note: 1. Fundamentally, arbitrary-sized stacks are a linked list-like data structure, which is managed with the GC just like cons cells. Which one is faster is a question that requires empirical analysis (and might have changed with the switch to Racket CS, further analysis would be great). 2. The existence of first-class continuations (call/cc) makes it harder to use mutation unobservably. In particular, an implementation of for/list that used mutation in the obvious way would reveal that if you saved a continuation in the middle of for/list iteration and then re-invoked it.


laurent.orseau
2022-2-23 16:38:09

@samth Re 1. I would expect the stack to be N×A, where N is the length of the list and A is the size of a stack frame, and may be significantly larger than a cons cell, no? Re 2. By that, do you mean that if for/list used mutable lists, it would force a call/cc to save a lot more things?


samth
2022-2-23 16:40:10
  1. Yes, stack frames are larger than cons cells. However, returning to a stack frame is very different than calling a function and passing a cons cell. Also, stack frames have a very different memory layout — many frames get allocated contingously.

samth
2022-2-23 16:40:57
  1. No, I mean that if for/list used mutation, you would be able to mutate the resulting list by invoking a saved continuation.

samth
2022-2-23 16:41:16

There’s a discussion of some of these issues in the original blog post about Racket adopting immutable pairs: https://blog.racket-lang.org/2007/11/getting-rid-of-set-car-and-set-cdr.html


laurent.orseau
2022-2-23 16:46:45

awesome, thanks


laurent.orseau
2022-2-23 16:48:57

> For PLT Scheme v4.0, we’re going to try it. Woah, that must have been quite a leap of faith at the time!


samth
2022-2-23 16:49:29

The community was smaller then, and we had more breaking changes.


samdphillips
2022-2-23 18:18:32

I did some simple microbenchmarks on CS and found that building a list recursively using the stack was faster than using an accumulator and reversing it.


laurent.orseau
2022-2-23 18:20:53

By how much?


samth
2022-2-23 18:22:44

Note that microbenchmarks are potentially more misleading that usual in this case because of cache and GC effects.


samdphillips
2022-2-23 18:29:21

IIRC around 2x. But yes, a contrived microbenchmark. I find not having to track an accumulator and have an exit fix-up step clearer to read.


soegaard2
2022-2-23 18:39:54

A little challenge: http://blog.scheme.dk/2007/05/know-your-implementation.html

(It annoys me that I didn’t include the timings)


samdphillips
2022-2-23 18:52:38

I recreated the microbenchmark. The numbers are closer than 2x, but the accumulator version spends much more time in GC. sam@nintendog:~$ racket microbench-list.rkt accumulate 100000000 cpu time: 37456 real time: 37577 gc time: 34463 sam@nintendog:~$ racket microbench-list.rkt stack 100000000 cpu time: 25059 real time: 25142 gc time: 17467



spdegabrielle
2022-2-23 20:16:39

Racket discord just hit 989 members Racket discord invite link https://discord.gg/6Zq8sH5 - come join us or invite a friend.


massung
2022-2-23 20:18:50

Aside from just having multiple places people may feel comfortable, is there a push to move from Slack -> Discord for one reason or another (I very much prefer Slack if there’s a desired shift by the Racket team to move away from Slack)?


rokitna
2022-2-23 20:26:19

I’ve been puzzled with some of the comments about loop since Racket itself has for and a lot of other keyword-heavy macros.


sorawee
2022-2-23 20:27:34

I personally prefer Slack, but all Rhombus discussions are in Discord, so I feel there’s no choice but to move there.


rokitna
2022-2-23 20:28:06

oh, I see there’s a longer discussion on the next thread XD


soegaard2
2022-2-23 20:30:35

I too prefer Slack - it seems there is more discussions on Discord though.


massung
2022-2-23 20:32:13

Maybe slack is the same and I don’t know it, but I dislike how - with Discord - when I’m logged in everyone across all the groups I belong to (games, etc.) all see that I’m on and I just get blasted w/ nonsense. Slack feels more focused, which is crazy since I feel like Slack is a major distraction, too. :wink:


sorawee
2022-2-23 20:33:16

You can turn yourself offline?


sorawee
2022-2-23 20:34:40

Two things I really missed from Slack are: thread and channel joining.


sorawee
2022-2-23 20:35:41

In Discord, you join all channels by default, and need to mute those that you don’t want to see.


massung
2022-2-23 20:36:25

If this is a bad channel for this question, my apologies…

I don’t know who - if any of you - use or have tried out r-cade, but I’m currently in the process of a few refactors I’ve wanted to do to simplify a few things.

One thing I’ve thought about doing given some requests/feedback I’ve gotten is to strip out the WAV/RIFF code generation for creating retro 8-bit sound effects and music and put it all into its own package people could use for other things.

Is there anyone here with a strong interest in me doing that?


ben.knoble
2022-2-23 20:38:38

> It can be done recursively easily enough, but then you run into the risks of blowing the stack, etc. Someone correct me if I’m wrong, but in Racket you can’t overflow the stack (you can obviously of course still run out of memory).


massung
2022-2-23 20:40:06

Depends what “the stack” is in the VM. If it’s the HW stack you can absolutely blow it. :wink:

And - depending on where Racket is run (i.e. embedded software) - what is considered “the stack” may also be different.


massung
2022-2-23 20:40:40

That said, I’m sure for most of us it’s the world of desktops.


spdegabrielle
2022-2-23 20:45:41

The racket team haven’t expressed a preference for discord. I started it on a whim and it grew unexpectedly.


spdegabrielle
2022-2-23 20:46:18

I think different platforms appeal to different demographics


spdegabrielle
2022-2-23 20:46:56

A lot of professional developers on slack A lot of students on discord


spdegabrielle
2022-2-23 20:50:24

I dislike the chat platforms (compared to mailing lists and forums) because they are effectively walked gardens and interesting and insightful stuff is effectively lost (this applies to slack, discord, irc, Zulip, etc.)


spdegabrielle
2022-2-23 20:52:05

But I hang about because the community seems to prefer chat platforms.


laurent.orseau
2022-2-23 21:04:56

I think all 3 media (Slack, Discord, Discourse) have their own different strengths. Discord is very casual, somewhat like IRC, so it’s only natural that it foster small talk. It’s very messy though, and the ‘threads’ aren’t that useful.

Discourse is more like a mailing list (just better) and is mostly for organized questions.

Slack is somewhere in the middle, where you can start with a casual chat and turn it into a separate thread very easily.


massung
2022-2-23 21:05:50

I like Discourse, I just always forget it’s there.




soegaard2
2022-2-23 21:12:01

On iOS I prefer to use the browser over “Discourse Hub”.


spdegabrielle
2022-2-23 21:13:08

:point_up_2:discourse app for your phone


spdegabrielle
2022-2-23 21:13:18

Has notifications


badkins
2022-2-23 21:20:14

I’m not sure which, of Slack and Discord, I dislike least, but I much prefer picking one chat service vs. having two.


greg
2022-2-23 21:20:28

My impression is that, to the extent Discourse is supposed to replace anything, it it the mailing list and Google Groups. Not Slack.


badkins
2022-2-23 21:20:58

Discourse and a chat seem orthogonal enough to warrant both.


greg
2022-2-23 21:21:45

Also IIUC we get full history forever with Discourse, for free (unlike Slack per-user fee). So that’s a bonus.


badkins
2022-2-23 21:22:14

Yes, and there’s the option to host our own Discourse down the road if the need arises.


sorawee
2022-2-23 21:22:16

Discourse has a size limit though — it’s not forever.


greg
2022-2-23 21:22:31

Oh. :disappointed:


greg
2022-2-23 21:22:45

My tentative working theory is that “Discourse is for paragraphs, but Slack is for sentences”. :smile:


greg
2022-2-23 21:24:47

I like Slack as long as I disable the default notification sound, which gives me a kind of “open office PTSD” (I am sort of but not entirely joking).


greg
2022-2-23 21:25:28

Oops I didn’t mean to send that to the main channel thread, errant mouse click. So clearly I am a Slack expert.


greg
2022-2-23 21:25:47

/me quietly slinks away now …


spdegabrielle
2022-2-23 21:26:29

No that is a good tip. I’m going to do that now.


sschwarzer
2022-2-23 21:27:50

> Discourse has a size limit though — it’s not forever. Do you know what this limit is? And it would be interesting if you get notified if you approach that limit.


spdegabrielle
2022-2-23 21:28:12

Discourse is reportedly not hard to host yourself. I know a guy who runs up instances for charities


soegaard2
2022-2-23 21:28:54

Compared to Slack the pricing for Discourse is peanuts. https://www.discourse.org/pricing


sorawee
2022-2-23 21:29:43

@sschwarzer: 5GB storage


soegaard2
2022-2-23 21:30:03

What happens if I go over the plan limits? Your site will never stop working! We understand your site can get a lot of visitors quickly for any number of reasons, and success is something to celebrate! If your site exceeds plan limits for several months in a row, we will contact you to discuss flexible upgrade options.


samth
2022-2-23 21:30:24

Right, there’s no stack overflow in Racket; Racket manages the stack so that the OS stack limits are not a limit for your program.


spdegabrielle
2022-2-23 21:30:24

Still cheaper to run it yourself on a do instance


spdegabrielle
2022-2-23 21:30:46

But the current free hosting is even cheaper


massung
2022-2-23 21:31:39

“cheaper” implies only the cost of actually hosting. uptime, maintenance, domains, tls support, debugging time, etc. might be worth the added costs


soegaard2
2022-2-23 21:31:47

Also: Yes! If you are legally recognized as an educational institution we offer an 85% discount. If you are legally recognized as a non-profit organization that is exempt from federal taxes, we offer a 50% discount. may or may not apply.


sschwarzer
2022-2-23 21:32:33

> @sschwarzer: 5GB storage Thanks! I wonder how much the Racket Discourse instance currently uses.


samth
2022-2-23 21:32:59

@massung I like Slack better, but other people created the Racket Discord and it’s become more active/popular than Slack. So I use it somewhat more just because of that.


sschwarzer
2022-2-23 21:33:03

Anyway, I bet 5 GB is much, much, much more than what Slack gives us in the free plan. :wink:



spdegabrielle
2022-2-23 21:39:44

I ask because now is the time to get started


spdegabrielle
2022-2-23 21:41:32

I would be supportive of either in person or virtual, but I am unlikely to be able to attend in-person.


spdegabrielle
2022-2-23 21:54:17

Just to have the last word… There are many programmers out there who don’t even know Racket exists. I - like you - am saddened by this situation. So while tracking all the channel used by the disparate racket community is impossible, coverage is not too bad.

The exception is the DrRacket Facebook group. There is also a Racket LinkedIn group. As Dr Seuss said: ‘I don’t know why go ask your mother.’


bsilverstrim
2022-2-23 23:24:38

@bsilverstrim has joined the channel


spdegabrielle
2022-2-23 23:37:38

What about an RacketCon in Europe? Or is that a bridge too far?


gknauth
2022-2-24 01:21:46

@spdegabrielle You still have the last word. Just mentally drag my late opinion somewhere higher on the last. At work we use Slack, but we don’t have the history problem, because I guess we are paying through the nose. On the Racket Slack, if I go back a couple of months, the history stops. That’s my major annoyance with Racket Slack.


gknauth
2022-2-24 01:28:45

I would support either. What I care about most is that the record of RacketCons be unbroken. That is, when people in the future look back in time, they will see there were RacketCons year after year, that they didn’t go up to year N and then suddenly something went wrong. The library of talks from all the RacketCons put together is really great. Also great, for many years what’s delighted me about RacketCon compared with other conferences is that I’ve loved every talk, I was never bored, I was heartened to see the content was consistently good. That’s the real magic of Racket for me. One thing I like about the in-person conferences is the chance to share beers with other Racketeers.