Hacker News new | past | comments | ask | show | jobs | submit login
Racket frustrates me (winny.tech)
122 points by lycopodiopsida on June 30, 2023 | hide | past | favorite | 127 comments



My info is a bit out of date, but I'll try to go through big block of concerns...

> Do I want to iterate every 18 hours waiting for the pkgs.racket-lang.org build server to finish building my package? Then do it again because the build failed (my mistake, but now my users have to wait until later that week…)?

( https://pkgs.racket-lang.org/ is one way of sharing an open source package, through a public repository analogous to PyPI. There is a small delay upon uploading a new package/version, because the server builds the package, runs the package's tests, formats the package's documentation, etc.)

I don't know why it would take 18 hours, and I don't see a bug report linked.

Maybe the author was just doing a commit to their own random GitHub repo, without telling the Racket packages repository, and so waited for the packages repository to do a periodic scan of all third-party packages' random Git/Web sources for new versions? (I'd always go to the Racket package repo Web UI, and push a button of some kind there, and the new version would go through promptly.)

> Do I want to worry about packaging and deploying Racket code or could I simply do the same in most other languages without any fuss? I tried packaging Racket in Nix, it did not go well.

What didn't go well? Racket runs on a wide variety of systems. I once experimentally packaged it for plastic OpenWrt routers, and it was straightforward. https://www.neilvandyke.org/racket-openwrt/

> Do I want to invest trust in a programming platform that considers umask of optional importance? https://github.com/racket/racket/issues/4511

That was a helpful bug report, and looks like Matthew Flatt followed up on it promptly, and the bug report even got Matthias Felleisen's interest.

Then, given Racket's emphasis on cross-platform and backward-compatibility, Matthew's decision to add the semantics support without breaking production for all of the existing users. That seems reasonable and predictable to me.

> Do I want to force users to adopt an application written in a language that uses at the very least 122M resident memory on my workstation? Or do I consider Python or Guile which both use less than 10M?

I don't know how the author is running their code, so this might be the best Racket can do with the code, or maybe...

DrRacket (IDE) can run code with very heavy debugging instrumentation, and you can also do similar things from the command line.

DrRacket also has a feature that limits memory usage to a specified amount, IIRC defaulting to 128MB, which is great for alerting programmers on workstations with multi-gigabytes of RAM that they might be doing something inefficient.

Code can be run from source files (silently compiling/recompiling on demand and caching for later runs), from the compiled form, or into a more packaged form (which IIRC does some tree-shaking).

What libraries you pull in can also mean surprises (e.g., I see a later comment by author in which they mention a third-party package having inappropriate dependencies).

If you want to run in really tight memory, that will affect how you program, and will include an awareness of the garbage collector.

But also, small executable size probably isn't a top priority for Racket.

> Do I want to force users to package Racket which itself has no clear custody of its build inputs - can you produce a Racket tarball yourself that is exactly like the one on the release site? Who knows what’s in those tarballs!

I suspect the author is talking here about what's now called "reproducible builds".

> Do I want to invest in a community without a mechanism to apply community feedback constructively? RFCs?

The author got Matthew Flatt and even Matthias Felleisen giving prompt attention to the bug report they linked.

They'll find similar responsiveness on the mailing list.

> Do I want to write 4x the code (in Racket) because I forgot my secret move was actually all the pypi packages that I took for granted in Python?

Depends what you need to do. The normal case, with programmers who are performing at normal levels and with normal practices is just to using PyPI (or NPM, or Cargo, or whatever), and bang out a solution to a normal problem.

But if you need to do something unusual, or you have programmers who are super-effective and can even judiciously bang out that reusable code as needed (better than the reused packages), then consider getting out of their way -- whether they just do it in Python or NodeJS, or they say this will go better in Racket, Rust, or something more exotic.

> Do I want to wonder about what was meant by “contributions welcome” when most of the core racket codebases at best have a README, at worst have no contributor documentation?

Documentation is one of Racket's strengths. From near the top of https://docs.racket-lang.org/ , see, for example, the document "Building, Distributing, and Contributing to Racket".

> Do I want to invest in a platform where leadership is building Rombus a.k.a. Racket2 instead of focusing on making core Racket just that much better and easier to adopt? (see also: Future of Racket.)

That's a good question. Racket is solid for production, has features that other languages still haven't discovered or done as well, and the author saw the prompt attention to their bug report. (The Rhombus thing has been going on much longer.)

> Do I want to wait 10+ minutes for my package to build in CI because some other package maintainer decided to pull in racket or racket-doc (which pulls in the entire big Racket distribution)?

Talk to the developer of that third-party package?

> Do I want to ship multi-gigabyte docker images because of the above kitchen sink package?

Talk to the developer of that third-party package, and there might be additional things you can do.

> Do I want to deal with being blocked due to not understanding how to use the less understood features of racket such as continuations, syntax-parse/syntax-case macros, units/signatures? You’ll want to know about all these things to write effective Racket code.

You'll need to understand first-class continuations to work on all the Racket internals as a compiler developer. But I don't recall ever seeing a non-internals use of that.

For the syntax extension mechanisms, you can learn that as-needed and at your own pace, because it's for things that most languages can't do (and does it better than languages that can). And you can definitely learn them incrementally, and start with the higher-level forms, which might just do what you need.

Units/signatures are ancient, and you probably don't need to use them. Just use the very nice module system (including submodules), and then decide whether you need more.

> Do I want to add business risk by writing a product in Racket? Does anybody work as an industry Racket coder at all? Would I struggle to hire more talent?

It's probably needless additional risk/unknown to use for business unless you have more or more people who are able to dig in and figure out technical stuff without Stack Overflow. (Though there are helpful forums, but you'll need to rise to the occasion more often.)

And one of the the secret weapons of beloved fringe languages/platforms is that they attract those mythical "10x" programmers on basis of technical/aesthetic/community merits rather than employability, so there's more of them than there are great jobs using Racket...

> Do I…. yeah there’s more but let’s move on ;)

(End of the contiguous block, switching to sparse quoting.)

> Where did the racket-money mailing list go?

https://www.neilvandyke.org/racket-money/

It's quiet most of the time, but picks up when a new person joins or there's a question/idea.

> Or really all the production users? I only found one production user.

There's more than that, including doing spectacular productivity and importance, but there's not many.

One thing the author of the blog post might be realizing is that the Racket professors are generally super-capable, they support production users like most professors couldn't, they'll provide technical support to any random person around the world. They'd love more commercial uptake, but that's been far from their top priority.


> > Do I want to wonder about what was meant by “contributions welcome” when most of the core racket codebases at best have a README, at worst have no contributor documentation?

Historically all repos were in `racket/racket`. When the repo was split into `racket/racket`, `racket/drracket` etc no readmes were added. This could be improved for sure -- but is it really that hard to ask in an appropriate forum such as Racket Discourse or Racket Discord?


If a question is asked on Discord, then the answer will be lost in Discord as well, not findable via search engines. I personally think the move to Discord was a mistake. I am not sure whether anything from the Racket Discourse forum is findable via search engines. But that is also merely a JS only forum, so not ideal. There used to be the Google groups thingy including a mailing list. Google, so not ideal either. But at least it had a mailing list.

It would be great to have a way of communication again, that does not require JS or Google or Discord or any other closed thing or walled garden. Especially for a programming language. Of those listed JS is probably the smallest inconvenience, so if I had a question now, I would probably prefer going to the damn discourse thing to ask it, rather than joining the Discord server.


> But that is also merely a JS only forum, so not ideal. There used to be the Google groups thingy including a mailing list. Google, so not ideal either. But at least it had a mailing list.

I was a long-time skeptic of moving away from the Google Groups list, so I try to be equally vocal in saying that the move to Discourse was handled really well IMO. In fact it is not "a JS-only forum": you do need to sign up in the web UI, but, after that, you can put it in "mailing-list mode" and ignore the website completely, including for starting new threads (see <https://racket.discourse.group/t/how-to-enable-mailing-list-...>). The email interface is important to many Racketeers; if other Discourse instances don't enable it, that's their admin's choice. The Google Groups list had been rendered nearly unusable by spam, and it was never free and open-source software, which Discourse is. (I wrote more detailed thoughts at the time in <https://groups.google.com/g/racket-users/c/RnIAQnZpvh0/m/A78...>.)

On the other hand, I share some of your concerns about Discord (the similar names are unfortunate). But there was never an organized "move to Discord": some people just started talking about Racket there, as people had before, and still do, on IRC and Slack and all sorts of places. People are also regularly nudged to move non-transient conversations to searchable and archived venues.


Thanks for the heads up regarding mailing list mode of Discourse. I will consider that!


> If a question is asked on Discord, then the answer will be lost in Discord as well, not findable via search engines.

That's why I put Discourse first.

> I am not sure whether anything from the Racket Discourse forum is findable via search engines.

It is.

> There used to be the Google groups thingy including a mailing list.

The mailing list is still running. But people have moved on to Discourse. You can configure Discourse to send you digest mails, if you prefer mailing lists.

Personally, I liked the mailing list. However, forums such as Discourse have replaced the role of the mailing list. See the forums for Swift, Rust, Processing, etc. They have all moved on to Discourse.


> I don't know why it would take 18 hours, and I don't see a bug report linked.

It seem systematic to the current architecture or developer experience - since the build environment is different than the local environment, there's going to be stuff that leaks through. For example, missing info.rktd dependencies, native dependencies, not pushing changes to dependencies that were on your local machine but not on pkgs.racket-lang.org yet (breaking api changes, additions), sometimes one gets 5xx errors from the frontend so that doesn't help.

When one clicks the "rebuild" button it simply queues the package to be rebuilt. Then you wait and hope everything is working. And come back to address the above issues.

> Then, given Racket's emphasis on cross-platform and backward-compatibility, Matthew's decision to add the semantics support without breaking production for all of the existing users. That seems reasonable and predictable to me.

Bottom line, any language that tries to break away from filesystem semantics of your host OS, is going to cause footguns. Sometimes it's okay to break backwards compatibility.

> What didn't go well? Racket runs on a wide variety of systems. I once experimentally packaged it for plastic OpenWrt routers, and it was straightforward. https://www.neilvandyke.org/racket-openwrt/

My rule is if it isn't packaged in my OS, it doesn't exist in a redistributable form. Racket isn't quite there yet; there's some effort underway to package Racket packages on various operating systems such as Nix, Gentoo, and so on.

> The author got Matthew Flatt and even Matthias Felleisen giving prompt attention to the bug report they linked.

It's true, however a formal process to discuss feedback and next steps for Racket would help folks like myself feel confident in the direction of Racket. Otherwise it's just people on the internet saying "trust me".

> Talk to the developer of that third-party package?

Unfortunately I have found not everyone is interested in fixing their packages. I think this problem might be best fixed by making changes to the package manager to improve the developer experience. My usual work around is to vendor code.

> Units/signatures are ancient, and you probably don't need to use them. Just use the very nice module system (including submodules), and then decide whether you need more.

Is it possible to set up a unloadable plugin architecture without signatures and units?

> There's more than that, including doing spectacular productivity and importance, but there's not many.

I look forward to learning more about these users.


> Build server and new packages:

The package server maintains a catalog of available packages.

When you submit a new package, it is more or less immediately registered in the catalog. This means other users can try your package quickly.

What takes time is the build server. The build server builds every package available on the package server.

The build server

  - builds the package in a sandboxed environment
  - builds documentation
This takes time, since packages can refer to each other. The advantage is that the documentation can use scope information to link all identifiers to the correct sentry in the docs.

It would be nice for some improvements though:

  - getting a mail when the build is done
  - incremental build of packages that no other packages rely on


On Gentoo xgqt pretty much has everything working as a portage overlay.

https://gitlab.com/gentoo-racket/gentoo-racket-overlay


It's disappointing that this is the state of things from a "working programmer" perspective.

Racket is, as far as I understand, a research and teaching language, as well as a viable scheme.

I've used it for some [command line tools and side projects][1], but I never tried to deploy anything with it.

Maybe a new [Chez][2]-based (as Racket now is) scheme is needed: one with much of Racket's nice syntax system, but focused on dependency management, targeted solely at "production" use, and other Serious Business Purposes.

Not it!

I'm not saying that Racket isn't a serious platform -- far from it. But the author's concerns, while expressed as a hot take, might be why Racket doesn't see wider industry adoption.

Or maybe it's a chicken and egg problem.

[1]: https://github.com/dgoffredo?tab=repositories&q=&type=&langu...

[2]: https://www.scheme.com/


> Or maybe it's a chicken and egg problem.

Maybe Chicken Scheme is the solution, then?

http://www.call-cc.org/


Chicken Scheme is great, but for production use you need an ecosystem.

For this reason Clojure and perhaps SBCL will dominate serious lisp dev.

Racket is a classic example of those peculiarly dysfunctional systems that emerge from an out of touch (dare I say ivory tower) academic community who think hype and prototypes are one step away from being enterprise ready.


I have daydreamed about making Racket more serious and calling it Tennis.


How about less serious and calling it Badminton?


I don’t see equivalent sobbing when somebody praise lets say Rust (random choice) for being productive, for the easiness of working with cargo or some other random stuff from the “working programmer perspective”.

Is this sad only when people have to talk about their pain points?


Totally ignoring your point and ranting about how much I hate Rust from easiness of working with.

Not the language itself (which I love) I can write code so quickly in it, and I didn't struggle for very long with fighting the borrow checker, which seems to turn so many other people off.

It's everything else. Long build times. "Modern" dependency management, which seems to mean package X only works with 0.6.x of package Z and package Y only works with 0.7.x of package Z and the current version of package Z is 0.8 and has a bugfix you really need. Though I should say that very few languages with a central repository don't have this problem; you make it easy to depend on libraries with unstable APIs and everyone will rush to do it.


So many people rushed to copy the node/Python dependency management system that nobody asked if it was really a good idea.

I totally agree that nailing everything down to the one particular minor rev causes way more problems than it solves. The worst part is I'm pretty sure you can be more general (give me version 3, or >3.3.1, not =3.3.1) but all of the automatic tools and online guides default to nailing it down to exactly one version because they're so damn paranoid about API changes.

I will say that CPAN somehow avoided this problem. I think that is mostly because Perl programmers are far less liberal with dependencies, you don't typically see programs pulling in dozens or hundreds of modules the way you do in node projects. But it's remarkable how many old Perl programs can still pull the dependencies and start right up.


> I will say that CPAN somehow avoided this problem. I think that is mostly because Perl programmers are far less liberal with dependencies, you don't typically see programs pulling in dozens or hundreds of modules the way you do in node projects. But it's remarkable how many old Perl programs can still pull the dependencies and start right up.

The fact that CPAN avoids this makes me think it's largely cultural. At least some of it may be that CPAN came of age in an era when internet access was less reliable and slower.

Some of it is also that development "in the open" has become more the norm. When releases are rare and only contributors have VCS access, then there are fewer opportunities to depend on unstable interfaces.


> But it's remarkable how many old Perl programs can still pull the dependencies and start right up.

I was surprised when I installed `git` and it pulled a couple dozen Perl packages from the distro repo.

There's definitely something to be said about Linux distro maintainers/packagers being sort of middlemen that help enforce compatibility for popular Perl (or Python2, etc) packages.

That is, there is a nonzero amount of greybeards between the developer and random end user, which cannot be said for your average npm or pip package.


That's already how Rust works. The post is referring to specific packages, not the whole resolution system; it is specifically 0.x versions that are incompatible at the minor revision. It is absolutely not the case that all the online tools and guides tell you to nail it down to one particular version - I have literally never seen a guide that told you to use =version notation. Finally, there's no difference between this and what you described as the Perl behavior: in both Perl and Rust, old packages with old dependencies still build.


> I will say that CPAN somehow avoided this problem. I think that is mostly because Perl programmers are far less liberal with dependencies,

Probably because CPAN existed before SemVer.

Given that patch version bumps of a dependency might break a project, I think giving up on the SemVer paranoia would be the best solution.

If a dependency breaks you just release a new patch release of your software with the version pinned before the breaking version. Add a field to the package manager to hint at the dep solver that it should never downgrade to remove that depedency because it was added due to a breaking change to sort of make it retroactive.

And the failure of many projects to go 1.0 because of paranoia about breaking changes and therefore the failure of SemVer should really be more widely discussed.


Op here.

It's important to recognize pain points so we do not become complacent with low quality developer experiences. Worse, complacency with software lifecycle at scale - will our product deploy on a cluster? Will our software be maintainable 5 years from now?

Better we look at the pain points now than accept a poor DX for years to come.


Rust is a terrible first language for someone trying to learn programming


I don't use Racket much, but Python certainly frustrates me. So what I want to know is where people land when all these languages frustrate them. Builds taking too long seems like a perennial problem, for example.

(Currently I work in LÖVE, but programs there use at least 100MB, so it probably won't be to OP's taste.)


For me it was Go. The toolchain just stays out of my way. I find Go much easier to deal with at a project level than anything else. YMMV because I'm a command-line kind of guy.

Many of you will assume I'm a moron for choosing such a retrograde, simpleminded language. It's true! I am a moron compared to most HN readers. And your language is much more sophisticated than mine and had generics long before and didn't need a make keyword and stuff. No disagreement there.

All I know is I can get the job done faster in Go than with Python, and I have type checking too. Plus a sane-ish package manager and a single, stable ecosystem. Plus I can deploy a single executable that even lets me embed assets inside it. It just stays the hell out of my way.


OP here.

Python frustrates me too, but I just throw poetry and nix at it... keeps me not frustrated. :D

It looks like my LOVE game uses about 99M resident... not too bad given it's pulling in a lot of libraries such as SDL2, luajit, audio decoders, etc etc.

I think my concern is more with how do I embed Racket on low memory systems if a simple "racket script" is going to use a large % of the RAM to issue a `(sleep)`?


I work in Common Lisp, which also is 100MB+ programs so likely won't satisfy OP either.


Not if you use CLISP! It seems like bytecode vs. native compilation is a large contributor to memory usage (Racket uses Chez Scheme which compiles to native; same story for Node/Deno and V8).

I have a low traffic web service and I wasn't happy with ~125 MB of my 512 MB VM's memory being used up by SBCL, but I was able to just drop in CLISP and memory usage dropped to ~25 MB (and for reference: a "hello world" is under 10 MB with CLISP, similar to Python).


I was wondering about this. If you use clisp, are you getting from its main branch, or another more up to date branch, and if so which branch?


I just used what my (Linux) distribution packaged for me. I’d think it would be easy to build from source, on Linux at least.


The other day on IRC, someone (pjb) mentioned that clisp is the best CL, so now I'm very curious :)


FYI: It doesn't support package-local nicknames :(


Typescript. Kotlin. Java.

I cannot imagine going back to Python, Ruby or PHP after using modern languages like these.

Also, Java performance is good enough. Why did anyone ever abandon Java? It's easy to read, easy to write and pleasant enough to work with. If you need C level performance, by all means go with C, Go, Rust but Java to me is perfect, and Typescript is good enough for everything else.


People dislike Java because of all the...

  public void setPints (int pints) {
    this.pints = pints;
  }

  public int getPints () {
    return pints;
  }
...-esque stuff, and similar overly verbose constructs you get when you follow certain Best Practice guides.

Personally I kinda like Java, despite it having 4 different kinds of brackets and object.method() syntax. Maybe it's the combination of 1 file 1 class and using classes for everything.


Use Kotlin.


...because Kotlin doesn't use different kinds of brackets and object.method() syntax?

Regardless, I'll stick to Common Lisp and C for the time being, they're nice.


No, it has that.

It doesn't have the -esque syntax and similar overly verbose constructs.


> Why did anyone ever abandon Java?

Because I physically recoil when 80% of the code is "APIAccessConfigurator apiAccessConfigurator = new APIAccessConfigurator(apiAccessConfiguratorToken, new apiAccessConfigurationParameters().class)" and similar crap.


In part, because the Java ecosystem kept getting more complicated. Fifteen or so years ago I was a full-time Java developer, but I wouldn't know half of what I need to know today, and I'm not very interested in going that route. Java kept the language simple-ish (not really, because they have added a lot on top of it), but the stack grew rapidly, and you have to know the tooling to function in that environment.

Python remains a lot simpler. It is getting more complex, but it has miles to go before it reaches Java-land.


> Why did anyone ever abandon Java Boilerplate code.

Java applications (mostly picking on tomcat here) being pretty much their own world of configuration, with lots of different places to look.

Slow startup time pretty much kills it for lots of things I'd wanna write.

It's memory management stuff sucks, from a sysadmins perspective. Any java application I deploy takes easily multiple times more memory than anything similar in a non-JVM language.


I've seen some slim running JVM stuff by developers who actually bothered to care about it - under 25Mb slim.

Yes, lots of Java apps basically go "lol .... KABOOM" if you give them less than half a gig of RAM to play around in, but IMO that's a tragedy of the commons like thing within the ecosystem where common approaches mean you end up with layer upon layer of libraries none of which thought -their- RAM use was particularly egregious.

At this point, it's not the runtime's fault (G1 is excellent), and while you could argue that the language design led developers to do things wrong I think it's more a culture phenomenom - I think I might consider blaming J2EE and its proponents rather than the language itself.

Of course, from a sysadmin point of view, the runtime+application combination's memory usage is what matters in practice, I'm just saying the reason why you find yourself staring at the screen thinking "wait, it's using how much?!" is more complicated than "Java's memory management stuff sucks"


anecdotally, towards the next generation of statically compiled languages, which have picked up a few affordances from the dynamic/scripting set. a lot of devops folk seem to be moving to go, for instance, and application developers have been showing some interest in rust, though personally I think it's a bit too low level to become universally popular. I wouldn't be surprised if something like crystal or native kotlin become popular down the road.


[I'm biased, so I'd focus in some parts I disagree.]

> Do I want to iterate every 18 hours waiting for the pkgs.racket-lang.org build server to finish building my package? Then do it again because the build failed (my mistake, but now my users have to wait until later that week…)?

I agree it should be improved, but I think that deploying to the package system is something that happens not very often. The idea is to keep only a safe and stable version there.

> Do I want to write 4x the code (in Racket) because I forgot my secret move was actually all the pypi packages that I took for granted in Python?

It would be interesting if the author post in github a feature request for the 3 missing packages that are more painful. (3 is a good numbers to find someone else that care about one of them, more than 3 or 4 would be annoying)

> Do I want to wait 10+ minutes for my package to build in CI because some other package maintainer decided to pull in racket or racket-doc (which pulls in the entire big Racket distribution)?

Sometimes I use minimal-racket, and it's this is annoying. Most people just use the main distribution so this is not a problem. IIUC the author cares about a small footprint, so it's a special case. I use Ctr-C, but I guess there is a trick with raco pkg to do fix this automatically.

> Do I want to deal with being blocked due to not understanding how to use the less understood features of racket such as continuations, syntax-parse/syntax-case macros, units/signatures? You’ll want to know about all these things to write effective Racket code.

You can skip most of them to write effective Racket code.

I use continuations only as an emergency exit inside nested fors, no fancy stuff. (I agree the fancy stuff is weird.)

I think units/signatures is almost deprecated. It will be there forever to keep backward compatibility, but I expect new code to use modules instead of units.

About macros:

If you are programing alone just use syntax-rule. syntax-case is not so difficult unless you want to do weird stuff. I've done weird stuff in the past, but five years later I regretted it.

If you are programing in a team, it's important that the macros have good error checking, because otherwise in case of an error it will expand crap that will expend to more crap and after a few rounds of crap expansion the poor soul that is using it will get a unintelligible error about code they didn't wrote. Here is where syntax-parse is useful, to ensure the initial code makes sense and expand only good code and give a good error reporting otherwise. syntax-parse is almost a separate DSL, so it's a lot to learn. (You can write the same stuff with syntax-case, but syntax-parse automates a lot of stuff.)


> It would be interesting if the author post in github a feature request for the 3 missing packages that are more painful. (3 is a good numbers to find someone else that care about one of them, more than 3 or 4 would be annoying)

here's a few that come to mind.

1. mumble client

2. CLI framework similar to cobra, click, etc

3. Bot framework similar to Cinch

4. WebRTC

5. off the shelf plugin framework

6. ssh library

7. gopher library (or just libcurl)

8. ntlk (natural language toolkit)

9. file/libmagic clone

10. image manipulation library

11. expect-like library for automation

12. probably more, these came to mind

> I use continuations only as an emergency exit inside nested fors, no fancy stuff. (I agree the fancy stuff is weird.)

Part of the racket web server requires continuations to use - so that's a blocker for anyone wanting to check out the racket web server in its entirety.

If one needs to do anything nontrivial with state, one might end up wanting dynamic-unwind, which means understanding continuations. (Example https://github.com/winny-/umask/blob/master/umask-lib/umask/... )

> I think units/signatures is almost deprecated. It will be there forever to keep backward compatibility, but I expect new code to use modules instead of units.

Can you do plugin architecture without signatures/units?


> 6. ssh library

I found this https://pkgs.racket-lang.org/package/remote-shell

> 7. gopher library (or just libcurl)

Is someone still using gopher? It's weird that there is no libcurl wrapper. I expected to find one. (I used the http libraries that are included in the main distribution, but some people may prefer to use libcurl instead.)

> Part of the racket web server requires continuations to use - so that's a blocker for anyone wanting to check out the racket web server in its entirety.

Yes, and the webserver has two kind of continuations. In one type the info is stored inside the memory of the racket webserver. In the other type the info is stored in the URL or something, but you must use a subset of Racket to be encoding friendly.

I use the webserver, but I just ignored both of them. Each webpage is a different rkt file. So fancy servlets, no continuations. All the info is stored in cookies or the hard disk.


I tried using racket to do something useful and totally failed each time. Even if you are a single programmer making some kind of prototype I think we have gotten beyond the stage of picking a small language.

Also, doing a rewrite into an Ml style language is really strange when they should probably be trying to become more competitive with other languages by building more batteries for it or fixing fundamental problems that were outlined here.


>Even if you are a single programmer making some kind of prototype I think we have gotten beyond the stage of picking a small language.

in what world does racket get classified as a 'small language'? it was one of the originals who focused on the 'batteries included' aspect for the sake of being more applicable in education.


> Also, doing a rewrite into an Ml style language is really strange when they should probably be trying to become more competitive with other languages by building more batteries for it or fixing fundamental problems that were outlined here.

From what I know, Rhombus and Racket are parallel, mutually-beneficial projects. The development of Rhombus has contributed smaller features into Racket, while showcasing Racket's capability for language development. Rhombus is, in a sense, filling in gaps that Racket may be missing.


Macros feel magical, this idea you can make your own language in no time so long as it's s-expression based, and make your own personalized vocabularies, expression flows, fit to purpose. Personal computing at its best!

But from a working-with-other people perspective, imagine opening some code and finding random invented constructs you have to reverse engineer and step through. These days it seems everyone creates yaml or json files, and then implements some kind of declarative engine or interpreter to implement their DSL. This can be both easier to read and debug. For more complicated use cases you can tie it to a graph database that supports reasoning, instead of json/yaml

Even so, is there really something that can be done with macros that can't be done, just as elegantly, with simple state machines, or object composition style in other languages?


> But from a working-with-other people perspective, imagine opening some code and finding random invented constructs you have to reverse engineer and step through

This feels just like working with another API to me. Macros can be arbitrarily complicated I suppose, but is the typical usage any more complex than learning an API? I don't really know, I guess, but my experience hasn't shown this to be that challenging.

I can appreciate that programming languages are ultimately codifying a particular way to think about computation, and macros express more idiosyncratic ways of thinkingabout comouting that may not be readily understandable.

I do think Racket has issues, but this isn't the first one I would cite.


> finding random invented constructs you have to reverse engineer and step through

Like functions and procedures?

I would pray that a good number of them are compile-time code transformers that execute in the comfortable development environment, so I don't have to reverse engineer and step through them on the embedded target.


I'm very curious what you think macros are that they'd be analogous to "simple state machines".

Macro expansion occurs at, well, macro expansion time which is prior to compilation (in a compiled lisp, as is the case in the context of Racket). The result of the expansion is what gets compiled. You could certainly express a state machine using macros (or with the aid of macros), perhaps even offering macro/compile time optimizations (hypothetically, you could take a regex in some form and convert it through a macro into straight code removing some runtime overhead). But macros are much, much more than "simple state machines". And I'm not sure what you mean about "object composition style", unless you're talking about the OO sense of preferring composition over inheritance. Which, again, what does that have to do with macros?


You can use macros to personalize your language, but for the slight overhead of run time, it's often better to use state machines or object composition style to recreate that same vocabulary.

Eg somekind of cron-like event library, made from a macro

    (wait 6 'hr' (run './myapp'))
In some OOP you could create an object style expression flow that's similar

    Scheduler.create(6,'hr').then(() -> system('./myapp'))
Or state machine

    scheduler(6, 'hr', steps= [() => { system(./myapp) }, ...]);
Then you customize the state machine steps with whatever, monad-like modifications are needed for that expression flow.

In the end macros save you a tiny bit of typing at the expensive of limiting static analysis tooling, limiting readability since the language can be modified, and most the time the runtime way of doing it 99.9999% as fast, easier to understand, easier to debug by stepping through it. That being said I like using macros, especially in emacs. No one will ever read my dot files, and I can live in my own world.


That should be a function:

  (wait 6 :hr (lambda () (run "./myapp")))
a macro to hide the lambda doesn't buy you much, and could be done by a macro that condenses lambda syntax:

  (wait 6 :hr (op run "./myapp"))


Wait method could expand into some kind of systemd library api calls which are more complicated. That's the thing about macros, 99% of the time they could just be functions, and when they're control flow macros that specialize away some runtime checks, you rarely save more than a few nanoseconds.


If 99% of the time macros could be functions, you're reading a Lisp codebase whose authors don't follow good practices like in Google's Common Lisp Style Guide (by Robert Brown, François-René Rideau).

https://google.github.io/styleguide/lispguide.xml?showone=Ma...

"You must never use a macro where a function will do. That is, if the semantics of what you are writing conforms to the semantics of a function, then you must write it as a function rather than a macro."


Can you give me an example of good macros in your opinion? It feels like any macro could be replaced with functions that does the same thing, albeit with a bit or runtime checks, or without being as terse as the macro.


How about defun? I don't want to program in Common Lisp like this:

  (eval-when (:load-toplevel :execute) ;; don't evaluate functions in compiler
    (setf (symbol-function 'add)
          (lambda (x y)
            (block add (+ x y))))
when I can program like this:

  (defun add (x y) (+ x y))
The nice thing is that I could remove the eval-when dance and have functions defined in the compiler by default, and the block could be omitted when I obviously don't need it. The remaining syntax is still ugly; I'd want my-defun.

Usually people whip out the LOOP macro, so let's skip that.

How about pattern matching (TXR Lisp). A simple expression like:

  (if-match (:pre :amble (@x) @(oddp @y)) obj
    (list x y))
churns out quite a mouthful:

  5> (macroexpand-1 '(if-match (:pre :amble (@x) @(oddp @y)) obj
                       (list x y)))
  (alet ((#:g0051 obj))
    (let* (#:result-0064
           x y)
      (if (if (consp #:g0051)
            (alet ((#:g0052 (car #:g0051))
                   (#:g0053 (cdr #:g0051)))
              (if (equal #:g0052 ':pre)
                (progn (if (consp #:g0053)
                         (alet ((#:g0054 (car #:g0053))
                                (#:g0055 (cdr #:g0053)))
                           (if (equal #:g0054 ':amble)
                             (progn (if (consp #:g0055)
                                      (alet ((#:g0056 (car #:g0055))
                                             (#:g0057 (cdr #:g0055)))
                                        (if (consp #:g0056)
                                          (alet ((#:g0058 (car #:g0056))
                                                 (#:g0059 (cdr #:g0056)))
                                            (progn (set x #:g0058)
                                              (if (equal #:g0059 '())
                                                (progn (if (consp #:g0057)
                                                         (alet ((#:g0060 (car #:g0057))
                                                                (#:g0061 (cdr #:g0057)))
                                                           (progn (set y #:g0060)
                                                             (alet ((#:res-0063
                                                                     (alet ((y #:g0060))
                                                                       (oddp y))))
                                                               (if #:res-0063
                                                                 (if (equal #:g0061 '())
                                                                   (progn (progn (set #:result-0064
                                                                                   (list x y))
                                                                            t)))))))))))))))))))))))
        #:result-0064 
I wouldn't write it in that mechanical way by hand; but writing it in a nice way would take thought.

The optimizer does a good job of reducing that kind of code, and the code walks through the list only once, using temporaries for successive cdr values.

The way I might write it might look terse, but involve redundant accesses to the list from the start.

Common Lisp's simple destructuring pattern operator destructuring-bind is also a macro.

  [1]> (macroexpand '(destructuring-bind (a (b c (d))) list (list a b c d)))
  (LET ((SYSTEM::<DESTRUCTURING-FORM> LIST))
   (IF (NOT (SYSTEM::LIST-LENGTH-IN-BOUNDS-P SYSTEM::<DESTRUCTURING-FORM> 2 2 NIL))
    (SYSTEM::DESTRUCTURING-ERROR SYSTEM::<DESTRUCTURING-FORM> '(2 . 2))
    (LET*
     ((A (CAR SYSTEM::<DESTRUCTURING-FORM>)) (#:G3318 (CADR SYSTEM::<DESTRUCTURING-FORM>))
      (#:G3319
       (IF (NOT (SYSTEM::LIST-LENGTH-IN-BOUNDS-P #:G3318 3 3 NIL))
        (SYSTEM::ERROR-OF-TYPE 'SOURCE-PROGRAM-ERROR :FORM SYSTEM::<DESTRUCTURING-FORM> :DETAIL #:G3318
         (SYSTEM::TEXT "~S: ~S does not match lambda list element ~:S") 'DESTRUCTURING-BIND #:G3318 '(B C (D)))
        #:G3318))
      (B (CAR #:G3319)) (C (CADR #:G3319)) (#:G3320 (CADDR #:G3319))
      (#:G3321
       (IF (NOT (SYSTEM::LIST-LENGTH-IN-BOUNDS-P #:G3320 1 1 NIL))
        (SYSTEM::ERROR-OF-TYPE 'SOURCE-PROGRAM-ERROR :FORM #:G3319 :DETAIL #:G3320
         (SYSTEM::TEXT "~S: ~S does not match lambda list element ~:S") 'DESTRUCTURING-BIND #:G3320 '(D))
        #:G3320))
      (D (CAR #:G3321)))
     (LIST A B C D)))) ;
  T
This example is from CLISP. Ut uses a lot of CADDR from the root of the list. Still a mouthful that is not nice to write by yourself, even in the nicest, tersest possible way.

Defining object, with complex bells and whistles (TXR Lisp):

  2> (macroexpand-1 '(defstruct myclass (these are super classes)
                       (:static static-slot 42)
                       uninitialized-slot
                       (initialized-slot 73)
                       (:method meth (me : optional-arg) (list em optional-arg))
                       (:postinit (me))
                       (:fini (me) (put-string `finalizer invoked on @me`))))
  ** expr-1:1: warning: defstruct: inheritance base these does not name a struct type
  ** expr-1:1: warning: defstruct: inheritance base are does not name a struct type
  ** expr-1:1: warning: defstruct: inheritance base super does not name a struct type
  ** expr-1:1: warning: defstruct: inheritance base classes does not name a struct type
  (sys:make-struct-type 'myclass '(these are super
                                   classes)
                        '(static-slot meth)
                        '(uninitialized-slot
                          initialized-slot)
                        (lambda (#:g0019)
                          (when (static-slot-p #:g0019 'meth)
                            (static-slot-set #:g0019 'meth
                                             (sys:meth-lambda
                                               meth myclass
                                               (me : optional-arg)
                                               (block meth (list em optional-arg)))))
                          (when (static-slot-p #:g0019 'static-slot)
                            (static-slot-set #:g0019 'static-slot
                                             42)))
                        (lambda (#:g0019)
                          (finalize #:g0019 (sys:meth-lambda
                                              myclass :fini
                                              (#:g0019) (let ((me #:g0019))
                                                          (put-string `finalizer invoked on @me`)))
                                    t)
                          (let ((#:g0020 (struct-type #:g0019)))
                            (unless (static-slot-p #:g0020 'uninitialized-slot)
                              (slotset #:g0019 'uninitialized-slot
                                       ()))
                            (unless (static-slot-p #:g0020 'initialized-slot)
                              (slotset #:g0019 'initialized-slot
                                       73))))
                        () ())
To write the function call, you have to know the API, which is ugly. You need to write a bunch of lambdas. You will not get a warning for the function that you're using undefined bases (and other useful diagnostics).

defstruct supports custom clauses you can define using define-struct-clause. There are some predefined ones for declaratively doing delegation between classes and what not.

The implementation of assignable places ("generalized variables") is done through macrology. All the place-mutating operators like setf, incf or push are macros. They analyze the syntax of the place and use the material from the back-end definitions to write the code, ensuring that complex places are not evaluated multiple times. Programs can define new kinds of places.

Object access? In TXR Lisp there is a macro qref. It corresponds to a dotted read notation:

  1> '(qref a b c (d arg))
  a.b.c.(d arg)
  2> (expand 'a.b.c.(d arg))
  (let ((#:g0024 (slot (slot a 'b)
                       'c)))
    (call (slot #:g0024 'd)
          #:g0024 arg))
If we suspect a.b might be null:

  12> (expand 'a.b.?c.(d arg))
  (let ((#:g0025 (slot a 'b)))
    (if #:g0025 (let ((#:g0026 (slot #:g0025 'c)))
                  (call (slot #:g0026 'd)
                        #:g0026 arg))))
Lisps are built on macros. And macros are something users have access to also, to do the same things for themselves.

You should use the quality, documented macros that are already provided. That's why that coding conventions document says that you should use macros liberally, just define them sparingly.

Lisp being built on macros has some nice advantages like if you have to work with an older version of some Lisp which didn't have some language feature, you may be able to backport those macros to polyfill that.


Wow, thank you for the detail


> Even so, is there really something that can be done with macros that can't be done, just as elegantly, with simple state machines, or object composition style in other languages?

Many simple macro use cases are covered by lightweight lambda syntax, reflection, or language constructs, e.g. Go's defer, Python's with, and C#'s using. Complex macros can create DSLs or rewrite code for optimization, though. Series[0] is a Common Lisp library that rewrites lazy streams into an imperative loop. LOOP[1] and ITERATE[2] are DSLs for iteration. Schelog[3] embeds Prolog inside of Scheme. Serde[4] generates serialization code.

Here are some old threads on the Lightweight Languages mailing list that discuss use cases for macros. Participants include Trevor Blackwell, Paul Graham, Dave Moon, Todd Proebsting, Guy Steele, Dan Weinreb, and many others.

• Macros Make Me Mad [5]

• macros vs. blocks [6]

• how expressive are they [7]

[0] https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node347.html

[1] https://gigamonkeys.com/book/loop-for-black-belts.html

[2] https://iterate.common-lisp.dev/

[3] https://ds26gte.github.io/schelog/index.html

[4] https://serde.rs/derive.html

[5] https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/...

[6] https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/...

[7] https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/...


> is there really something that can be done with macros that can't be done, just as elegantly, with simple state machines

My guess would be a C# using block. I think the Java equivalent is "try with resources" but I don't remember. In Common Lisp it'd be WITH-OPEN-FILE, or a similar macro made on top of UNWIND-PROTECT ...

> or object composition style

builder pattern comes to mind :)


> But from a working-with-other people perspective

There could be macro for that:

  (with-other-people ((bob (lookup :accounting "bob"))
                      (alice (lookup :human-resources "alice"))
    ... some workflow about about last payroll ..
    ) ;; people are nicely disposed at the end of scope


There is no situation in which your language doesn't impact your productivity to some degree, sorry.

When you've put 40,000 lines into the program, and your language is suddenly impacting your productivity more than you like, you can't just drop all that and switch!

Your language has you hostage. "If you don't like the dent I'm putting in your productivity, go rewrite 40,000 lines in something else. Mouhaha!"


> Do I want to deal with being blocked due to not understanding how to use the less understood features of racket such as continuations, syntax-parse/syntax-case macros, units/signatures

I don't know much about scheme or racket, but am aware that continuations is one of its distinguishing features. I've seen people praising syntax-case as a great take on macros. I've never heard of units/signatures, though.

I know this was supposed to be a rant, but I wonder what drew the author to want to use scheme/racket in the first place?


Language ecosystem is more important than the language itself. Typing 5 lines of code because of fancy syntax is nice, but writing 20 doesn't take a significant amount of time. What does take tons of time however is figuring build systems, packaging, distribution, debugging, editor support, documentation, community, libraries, etc...

I'm surprised no new languages focus on ecosystem. It is much less fun than making clever syntax I suppose.


> I'm surprised no new languages focus on ecosystem. It is much less fun than making clever syntax I suppose.

Most new languages that I know of have a package manager/inbuilt dependency resolution mechanism and a fairly robust module system to try and support 3rd party dependencies.

Rust, Go, Dart, D. You can expect every language post 2010ish to have this.


Pretty funny to say package management is table stakes and use go as an example. For a long time they had nothing, then hacky workarounds, and now they have a first party hacky workaround


Getting packages working in Go is approximately as low-friction as it is with any other language: "go get <whatever>". Packages are qualified by their Git repository URL, which addresses an important supply chain security issue (but creates other issues). Go has supported a thriving ecosystem of third party packages since approximately the moment it went mainstream.

So, ultimately, all people are saying when they knock Go's package management is that they don't like how it works. That's fine, I don't like how all sorts of things work. But "nothing" is false, and "hacky" is so subjective as to be practically worthless. Obviously, the reason Go gets picked out in these discussions is that it's quite successful as a language, and, to me at least, it follows obviously that they solved package management to some level of the community's satisfaction, because languages without package management aren't successful.

If we wanted to hash out the pros and cons of different package management strategies, that's all the thread will be: just hundreds of back-and-forth messages about package management. It's a big topic.


>… Obviously, the reason Go gets picked out in these discussions is that it's quite successful as a language…

When I wrote Go, the official language policy is that you do not need versioning and should not make breaking changes. Throw everything into a vendor/ if you must. For me, that kind of stance on package management definitely warrants attention.


People have all sorts of thresholds past which attributes of package management warrant attention. Some of them are even reasonable. But it's not legitimate to claim that a language doesn't have any effective package management, at least when that language is (a) popular and (b) idiomatically dependent on 3rd party packages, simply because those thresholds get crossed. Go self-evidently has effective package management, and has for over a decade.

There are lots of things I don't like about NPM. But imagine claiming that Node doesn't have real package management. You can't even write a for-loop in Node without a package. Go isn't quite that far gone, but it's pretty hard to write idiomatic Go of any significance with zero packages.


I'm not sure when you wrote go, but versioning is even more clearly part of the package management than with other languages. Major versions of a library have different import paths, such that you can look at the top of any file and know exactly which major version of a dependency it's using.


I think you're right that Go's current approach is similarly fine as the other ecosystems with basically fine approaches.

But they really did launch with no solution and take a fairly long time to figure it out.


I agree. I've been using Go as my primary language since 2011. Its package management has always been fairly simple, but deficient. Go modules have made things more accessible, but it lacks serious features and actually has quite a few bugs and edge cases. Every time I use cargo I'm blown away, it's incredible. People often gloss over Go and lump it in with Rust which is giving it far too much credit.


I'm not saying it's great, but saying it was thought about even though one of go's design philosophies was to be as minimal as possible. That is, it's something that was thought of as part of the language development.

The same can't be said of slightly older languages like C#, Java, or python. Perl, for all its foibles, was probably the first language with a more modern package management system (CPAN).


I think its more like pretending that things are simple (or that a simple solution is enough), when in truth some things just aren't simple and pretending otherwise is unwise. Like how they ended up adding a package manager later on, which of course was never part of the initial design because they thought nobody needs to version their dependencies. Or how they ended up adding generics later on, because they thought nobody will ever need to do "abstract things".

The problem is if you don't consider these things in your initial design, the solution you end up with later tend to be worse.


Is it even fair to call them packages? It’s a slightly more convenient method of repo sub modules, but there isn’t any object reuse or sharing outside of a workspace.


Would you say that that's a good thing? I'm so tired of trying to figure out which package manager is responsible for which thing and keep track of their various idiosyncrasies.

I think nix might be the way--just have one package manager to rule them all--but I'm not aware of any language ecosystems that are leaning into this.


> Would you say that that's a good thing?

Yup, the alternative is a C/C++/or Haskell scenario where instead of one generally accepted "right" package manger you end up with 20. You might get lucky like Java did with maven but that's no real guarantee.

> but I'm not aware of any language ecosystems that are leaning into this.

Bazel does. However, bazel (AFAIK) generally does things by leaning on the existing build systems of various languages.

For software, unfortunately we are in a "jack of all trades master of none" scenario. I don't think there's a one size fits all solution.


> the alternative is a C/C++/or Haskell scenario where instead of one generally accepted "right" package manger you end up with 20.

To be fair, Haskell doesn't have 20. It has two.


Just a sec, let me go create 18 more ;)

But yeah, fair point. And maybe we can rely on newer languages to converge on a package manager faster than the languages of old did due to the internet.


Did Java get lucky? I thought it was out with maven and in with gradle--though it's been several years since I've been in that world.

It's just kind of awkward how they all do the same thing in slightly different ways. But not meaningfully different.

They all have some kind of lock file, and they all have some kind of cache that you have to handle specially if you don't want to download the universe in CI, and they all let you specify version restrictions but each uses different files to store these things, terminology to describe them, and uses a different configuration language for you to express them.

Learning languanges would be so much fun if I didn't have to learn a new package manager each time.


The important thing that happened for Java is the centralized repository. Gradle and maven both use the maven repository standard. So you can publish a new package and either system can pull it in. This isn't as true if you talk about plugins to the build systems so it's not as if java is unscathed.

However, I'd say it's a lot better situation than what exists for C/C++ ATM.


I'll agree that having strong consensus about the map:

(Package name, version number) -> (cryptographic hash of package bits)

Both is, and ought to be, table stakes for modern language ecosystems. But when it comes to tooling for hosting, fetching, installing, and contributing re: those maps, there's no reason to have each separate language community build an overlapping set of tools that solves those problems.

I understand that when you have a new language, building all of that tooling is probably a fun way to flex it, but it's a bigger problem than most language communities really have the interest in to solve properly. So it ends up being a hack job every time.

I'd argue that the only reason Java doesn't feel like a hack job re: packaging is that they've (wisely) solved a smaller problem. I can use pypi to install a python package with Java dependencies, but I can't (so far as I know) use maven to install a java library with python dependencies.

Better would be to have some lightweight standard for mapping package names to hashes, and then decouple the hosting/trustworthyness indicating/installing/packaging concerns such that language designers don't have to worry so much about those things since they're not really language specific problems.


Typing 20 lines doesn't take much more time than typing 5 lines, but dealing with 5x more code thanks to an inexpressive language sure is an ongoing drag on productivity that just gets worse over time—while figuring out tooling/packaging/etc is, mostly, an up-front cost. Obviously better tooling is better, but my experience has been that ≈everyone overvalues up-front costs vs ongoing costs because they're more legible.

Frankly, I wish new languages focused more on flexibility and expressiveness even if that made things harder up-front!


> Frankly, I wish new languages focused more on flexibility and expressiveness even if that made things harder up-front!

Design languages for experts, not beginners. It might be more difficult to get started but if it's well designed it pays off.


> I'm surprised no new languages focus on ecosystem. It is much less fun than making clever syntax I suppose.

Honestly, this is a lot of the draw of Go and Rust for a lot of people; other than debugging (which I can't speak to from experience, but as I understand it, both are on par with C++), the points you hit on above are all things Go and Rust have really solid answers to compared to their contemporaries.


> What does take tons of time however is figuring build systems, packaging, distribution, debugging, editor support, documentation, community, libraries, etc...

Yes.

https://www.hillelwayne.com/post/learning-a-language/


Op here.

I disagree. You can get up and started in most language stacks fairly quickly. Keep focus, learn to sort advice into helpful and unhelpful buckets, and start building with a goal in mind.

For example - I hadn't touched fennel nor Love2d until about a month ago, and we made something happen.

You can do this too.


I recently learned Fennel and cranked out a Love2D game as well. Not only that, but I discovered it’s trivial to run the game on some spare nintendo consoles I have lying around, because Lua is insanely portable.

In contrast, I spent a ton of time trying to build other lisps that are designed to be embeddable on those consoles and got nowhere.

Fennel rocks. Such a pragmatic and well designed mini-language.


You can get started quickly, yeah. You can also build some stuff. It might be useful, even.

The article is about what it takes to really know a language. That's a bigger task. It takes months, if you work fast. That's how you write high-quality, maintainable software.

You can do this too.


Sounds like Cobol. I get asked (not that I'm an expert, but I occasionally live in the strange ether between the mainframe and the fad-driven front-end) 'how hard is it to learn Cobol?'. Not so hard. But knowing Cobol is about 20% of the job. Knowing all the mainframe ecosystem (z/OS, CICS, etc) is what takes serious time.


> I'm surprised no new languages focus on ecosystem. It is much less fun than making clever syntax I suppose.

In a way it is. After all if i make a new language, i can control what it can do and how it evolves but i can't control other people to force them use it.


> I'm surprised no new languages focus on ecosystem.

All of them?

Go, Rust, Kotlin, TypeScript


I had a professor at college who mandated Racket as the mode of learning for a software engineering course. I remember how Racket just ended up taking centre stage, figuring language-specific concepts and hacks out let the larger engineering concepts fade into the background.

I did not learn much in that course.


They used it improperly then. Racket is used for the first year intro CS courses at my school. They do not use the full language! They use the beginning student [1] and intermediate student [2] versions of the language.

These are highly restricted teaching versions of Racket which do not include the vast majority of the language features. They also use beginner friendly names such as first and rest instead of the cryptic car and cdr. These languages allow students to learn functional programming and basic concepts such as recursion, linked lists, association lists, higher order functions, trees, and graphs without worrying about programming language details such as side effects, fixed size numeric types, memory allocation, pointers, macros, compilers, etc.

For that purpose, Racket seems pretty hard to beat! For the professors, it also has the (nice) side effect that many students with previous programming experience are less likely to have learned Racket (or any functional language for that matter). This has been argued to put the students with and without programming experience on a more equal footing.

[1] https://docs.racket-lang.org/htdp-langs/beginner.html

[2] https://docs.racket-lang.org/htdp-langs/intermediate.html


For those thinking "you can do that in any language, just teach a subset of the language": no you can't! The Beginning Student Language (BSL) and Intermediate Student Language (ISL) are subsets of Racket in the sense that you can take any BSL or ISL program and run it in Racket and get the same result. If the program does not error. But if it errors, BSL and ISL will sometimes give better error messages, which they can do because the're simpler languages.

For example, consider the program `((+) 1 2 3)`. Racket gives a runtime error "application: not a procedure" because (+) evaluates to 0, which is then applied to "1 2 3", which errors because 0 is not a procedure. But BSL can give a (helpful) syntax error, because it doesn't have higher-order functions.

(I feel like someone's going to tell me that BSL or ISL isn't a subset of Racket for some obscure reason. They're at least essentially subsets.)


> They use the beginning student [1] and intermediate student [2] versions of the language.

To elaborate, one of the further benefits of multiple student languages like these is that concepts are taught incrementally. For instance, in the beginning student language, lists are explicitly shown as `(cons a (cons b '()))` before they are shown as `(list a b)` in intermediate student language.


My introductory course was in C/C++. I already had programming experience, but for those that didn't - your statement would apply well to it as well.

So many concepts got in the way of actually learning programming: You have to include header files for basic I/O to work. You need to understand functions to get any program to work. Pointer syntax sucks. Strings are complicated.

Coming from BASIC and QB, the barrier to entry in C/C++ was way too high. To be frank, Racket is more suitable, even if it's not the best option.


My professor for "Programming Language Concepts" was a contributor to Racket and it was center stage of the class. He did his lectures in DrRacket. He did a great job building up from principles to explain things, but was also very opinionated and had very high standards for homework. It was a tough class for me.


Did we go to the same college? :)

In seriousness, Racket is semi-commonly used in educational settings. Especially for "functional programming"-type courses.


Can you give specific examples?

I’m not doubting you. I’m interested in what parts of Racket could be confusing to beginners, maybe to make better courses


Northeastern, Rice?


He went to IIIT Hyderabad if I found the right Samsung-associated guy of the same name on LinkedIn. I went to Rice when Racket was Scheme and definitely it got pushed hard on me, at least in the intro CS course. :) But that was back when CS was one of the smallest majors, and now it's the largest at Rice and their curriculum is quite different!


> Coding is a tool to get stuff done.

Sure, that’s one of its roles. Racket helps me think. Maybe I’m lucky, and work on genuinely novel problems where a malleable language beats libraries.

So, sure, the Racket ecosystem leaves much to be desired, but getting stuff done is only one of programming’s roles.


People who use that argument are the same ones saying 'sure, I can do it in an afternoon/by tomorrow', and leave a trail of accumulating tech debt in their wake. It's a solid career move, what can I say? You get noticed for being such a sport and result oriented, and if you've got that service attitude, you get promoted/hired out of having to maintain that mess.


No. Some? Maybe. All? No.

I worked at a few companies where, when I needed to model a tricky system and build out an algorithm to do a tricky job, I turned to Racket as a draft. Once I got a POC done, we’d turn around and build it For Real™.

If all you’ve ever done is work as an implementer, then yeah, this flow doesn’t make much sense. But I think for people who work as problem discoverers, this is more common than not.


There's a live coding talk by John Carmack where he uses Racket for making small programs for the Oculus:

https://www.youtube.com/watch?v=ydyztGZnbNs

He seemed happy enough with the language.


Some good points, but i find the tone a bit off.

I have been eyeing racket for a while now, but could never quite make the jump. Racket (the programming language) has definitely a lot of potential, but i do believe the academic side of it and the focus on the "build your own language" part might be an impediment to racket growth.

If i was to suggest some stuff :

- Clearly split Racket (and typed Racket) the language, evolution of scheme and Racket the PLT framework use to build and construct new languages. Racket / typed Racket should focus on more production/libraries for regular programmer - More examples/libraries/package not directly related to PL and PL teaching. Create a website solely for Racket/typed racket, with the academic part. - DrRacket always felt and looked alien to me. I can't really judge the feature set, but having vscode as the defacto IDE i think would be it more welcoming - This one is will definitively be controversial but introduce a "curly brace"/C style syntax for Racket. Something implemented at the reader level. Same semantic/runtime behavior, and a reader/printer to go between Racket and {C|rust}SyntaxedRacket.

That's a lot of effort, so why do this ? I believe the world is looking for a better python. There is a lot of contender right now (julia, nim , etc... etc...). I think racket should also throw their hat in the ring.


> Clearly split Racket (and typed Racket) the language, evolution of scheme and Racket the PLT framework use to build and construct new languages.

It's very much possible to use Racket without interacting with macros and language-building features.

> Racket / typed Racket should focus on more production/libraries for regular programmer

Libraries are dependent on the community, which unfortunately is comprised of mostly academic folk at the moment.

> DrRacket always felt and looked alien to me. I can't really judge the feature set, but having vscode as the defacto IDE i think would be it more welcoming

I do agree -- if I remember correctly, DrRacket was primarily built as a pedagogic IDE, not one meant for more professional use. VSCode, however, does have the "Magic Racket" extension.

> This one is will definitively be controversial but introduce a "curly brace"/C style syntax for Racket.

Not necessarily C-style syntax, but you could take a look at Rhombus. It's meant to have a more approachable syntax compared to S-expressions.


> It's very much possible to use Racket without interacting with macros and language-building features.

I would say that macros are integral part of Racket, maybe event the best part.

But point is not about what is possible, it's really about what is the default/obvious part. It wasn't a comment on racket the language, but more about the environement around the language.

> Libraries are dependent on the community, which unfortunately is comprised of mostly academic folk at the moment.

I am not sure what point you are making here. Julia has a very very strong academic community, but the library ecosystem seems more production ready and actionable.

> Not necessarily C-style syntax, but you could take a look at Rhombus. It's meant to have a more approachable syntax compared to S-expressions.

Rhombus seems to be more than just alternate syntax. And how is the progress on this thing ?

I think to make racket more mainstream would need more than things that are "possible", or available etc... It probably need a cohesive story and experience around those, and that story to be at the for front on how Racket present itself : vs code extension as the main IDE, alt syntax + better libraries.


> his one is will definitively be controversial but introduce a "curly brace"/C style syntax for Racket. Something implemented at the reader level. Same semantic/runtime behavior, and a reader/printer to go between Racket and {C|rust}SyntaxedRacket.

This already exists, in a few forms (e.g. Sweet Expressions). I wrote a blog post listing a few, for whenever this topic comes up ;) http://www.chriswarbo.net/blog/2017-08-29-s_expressions.html


Let's agree to disagree on this one :)


Something that I find a little concerning about Racket is that AFAIK there are no alternative implementations. (Clojure and Common Lisp have a couple). Why is that? is it too complex?


Racket itself came from PLT Scheme which was based on the Scheme standard (R5RS or R6RS at the time of Racket's diverging, can't recall which, wasn't paying much attention). It's still a fairly new language (13 years old now) and was largely Scheme compatible most of that time (still is, actually, if you stick to `#lang racket`, but not 100%). It also has no published standard, in this regard it's somewhat like Rust and others where to make an alternative implementation you've got to reverse engineer (fortunately it's open source) aspects of the current implementation.

So it's not like you can just pick up R8RR (since it's on version 8, and keeping with the naming convention of Scheme standards as in R5RS) and start building something.

Common Lisp also has a standard for implementers to follow, and is about 25 years older than Racket. That's given implementers a bit more time.

Regarding Clojure, what's the status/quality of all the other implementations?


I believe Racket goes back further than 13 years. I clearly recall using Dr Racket in the early 00's


It was DrScheme in the 00s. They changed the name to Racket and DrRacket in 2010.

https://blog.racket-lang.org/2010/06/racket.html

https://www.racket-lang.org/new-name.html


Racket is a Scheme dialect (sort of), which has multiple implementations. People might be confused by the name Racket; it was clearer when it was called PLT Scheme.

Common Lisp has more than a couple of implementations, especially when we take a historic view. However, say, Steel Bank Common Lisp has only one implementation.


Some of the points are probably valid. But some I do not see as quite so valid:

Many other deployments work similarly. Make a docker container and put it anywhere. Are Racket docker containers necessarily that much bigger than lets say Python ones or GNU Guile ones?

Racket is packaged in GNU Guix btw., even, if I have had mixed experiences with the package, it should be mentioned. So someone already succeeded in packaging it in the package manager, that once was derived from Nix.

> Do I want to write 4x the code (in Racket) because I forgot my secret move was actually all the pypi packages that I took for granted in Python?

This really depends on what kind of code you are writing. When I wrote some Racket programs, I used mostly built-ins or perhaps 1 or 2 dependencies. Depending on the project, maybe one needs more of those dependencies and writing them oneself would be too much work. But for simple dependencies that are easy to write oneself, I wouldn't want to use a dependency from PyPI.

> Do I want to deal with being blocked due to not understanding how to use the less understood features of racket such as continuations, syntax-parse/syntax-case macros, units/signatures? You’ll want to know about all these things to write effective Racket code.

Hm, I don't know whether you want to. Do you want to be a capable programmer understanding more language concepts, being able to apply them perhaps in your next project or when learning your next language? Do you want to be able to express things elegantly than possible in Python for example? Do you want to know Scheme things? If not then OK, we all make our choices. I personally don't think it is good to avoid learning about something like continuations and stumble on without such knowledge as a programmer, but in more mainstream languages it probably is sufficient. However, one can also write lots of effective Racket code without going into macros. One does not have to follow the idea of having to make a language.

> But then the real hard truths started to appear. I was losing a lot of productivity. The productivity losses were incremental but certainly slowed down my velocity significantly over the last seven years. Sure the language has a lot of wonderful features, but there’s more to a good programming language than well, its programming language.

This is probably the most important point raised.

I hope though, that people who run into any shortages with Racket consider building the thing they are missing, if they are able to, to add to the ecosystem and to avoid the next person running into the same thing lacking. If no one ever writes their own, we will only ever stick with the status quo in terms of programming languages. (And fortunately people do write their own often.)


> I hope though, that people who run into any shortages with Racket consider building the thing they are missing, if they are able to, to add to the ecosystem and to avoid the next person running into the same thing lacking. If no one ever writes their own, we will only ever stick with the status quo in terms of programming languages. (And fortunately people do write their own often.)

Leadership needs to involve the community, then this can happen. Right now it seems that decisions around Racket are made at will - no process or formal discussion.


higher resident memory is not ideal, but is it a dealbreaker?

Anyhoot - I think clojure is the best lisp there is right now, but for better or worse it is tied to the jvm and seems to be stagnating.


Do you use Clojure? I'd give the title of best Lisp to Common Lisp, the Actually Lisp -- it's so Lisp it even has Lisp in its name and runs decades of Lisp code, something neither Clojure nor Racket can do any more easily than Python. I suspect many of the author's frustrations would go away if he just used CL... though he'd find new ones for sure.


High RSS being a dealbreaker for OP rules out most lisps. ECL might be low enough, but certainly SBCL and CCL will be too high.


Try CLISP. It uses a VM, so it's much simpler and uses less memory (but is slower, obviously).


High RSS makes me wonder how well the language is designed and implemented. Luajit gives you many of the things racket does (and can be easily extended to add any racket feature) without having crazy high RSS. Even though it may seem minor, it gives me a lot more confidence that at a minimum, luajit isn't _badly_ designed.


It is surprising to me that academics would not think that it is worth optimizing RSS for hello, world.


Life lesson: don't waste 7 years of your life and and personal growth on some weird fringe language, you will not get them back. It can pay off to make a serious commitment to a non-mainstream technology, but basically only if it allows you to do some very concrete things that give you a big competitive advantage and that you could not reasonably achieve otherwise. Or if you are both unusually good at predicting the next hot thing, and the timescales are short enough. Racket is not a good bet in either regard (even if it's a great language to learn certain concepts).

All the lost productivity the poster is bemoaning could have gone into mastering hard and useful skills instead and these things compound.


100% this. It was a risk taken and it didn't pay off.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: