Hacker News new | past | comments | ask | show | jobs | submit login
Racketscript (racketscript.org)
167 points by bwidlar on Nov 1, 2022 | hide | past | favorite | 53 comments



The github page has more information: https://github.com/racketscript/racketscript

And to answer the questions every Schemer will have, no the runtime doesn't yet support tail calls or continuations.


Continuations I can live without, but is Scheme at all usable without tail calls?


Not normal idiomatic Scheme, but I suppose some programs might have in-practice stack usage bounded not too high.

A Wasm target of the Chez/Racket compiler might be an easier way to get fast and proper evaluation in Web browsers.


tail calls are a foreign concept to me, and I assume that's because I'm coming from JIT scripts where that's not a common way to think of optimization. Just reading a bit of what they are, it sounds like you're branching within a function to others that return something of the same type - common enough - but get a huge memory/ processing bonus when you branch as long as the sending function doesn't continue? Is that sort of it? I'm really intrigued as to how and where this plays a big role in day to day coding or whether it's just an optimization once you refactor everything at the end.


TCO is a runtime feature that enables code authors to write functions that call out to other functions in such a way that the stack size is not increased. If you’ve ever crossed the `RangeError: Maximum Call Stack Size Exceeded` error, it’s possible that the executed code could’ve benefitted from TCO. TCO is especially invaluable for writing recursive functions (without it, recursion is almost always a bad idea, IMHO).

TCO is technically in the ECMAScript specification (since 2015) but hasn’t been implemented across browsers or runtimes (with a few exceptions).

Dr. Axel has written a good explainer of TCO in the context of Javascript here https://2ality.com/2015/06/tail-call-optimization.html


This can be worked around using trampolines.


This is the first time I've heard of "trampolines". How interesting.

Do you use this strategy for implementing recursion? Are there consequences or trade offs?

For others who are curious, see

1) https://stackoverflow.com/questions/25228871/how-to-understa..., and 2) https://raganwald.com/2013/03/28/trampolines-in-javascript.h...


Check "Using ParentheC to Transform Scheme Programs to C or How to Write Interesting Recursive Programs in a Spartan Host (Program Counter)" for more about trampolines.

It's not the only strategy though.

https://stackoverflow.com/q/6003037/23567


You would do this inside a transpiler, rather than manually.


Basically it is just another way of doing gotos, while keeeping the abstraction of function calls, without the space required for recursion stack data structures.

Having this as guarantee allows all the control flow concepts to be reduced to recursion and function calls.

A kind of purity when we reduce a programing language to the basic set of primitives that provide the building blocks to create any kind of programming paradigm, this is in a way the beauty of Scheme, moreso than Lisp.


Specifically, loops are a special case (with special syntax) of tail calls for language that don't support tail calls properly.

However as great as tail calls are they can't practically implement all control flow concepts. Eg they aren't really all that great for modelling exceptions, I think.


I would agree with sibling, and expand his comment a bit. Tail calls are just the specialization of continuation passing (or of having reified continuations programmer accessible. Actually, all control flow is the application of a continuation. So while I agree that tail calls are not used as the basis for exceptions, the parent concept of continuation passing does.


Fair point, however exceptions have their roots on continuations.


It is one of the few languages where tail calls are part of the language specification, so not really.


its the only way to do loops without blowing your call stack in scheme. technically yes as long as you're not doing anything interesting


Currently, the compiler does a dumb conversion to JS for loop of self tail calls. So if you call `map` on list of 1000000 items, your stack wont blow up. But I understand it is not enough for many non-trivial codebases. We were interested in doing more general TCO, and had some ideas around using trampolines (or iirc detecting mutually recursions and then trying to generate potentially a bit faster code). Had limited time, and was maybe too hopeful that eventually TCO will actually be supported by browsers (as it is part of standard).


If you want full Scheme running on top of JS in the browser: https://try.gambitscheme.org

It has tail calls, continuations and even green threads (SRFI 18 compliant), and a decentralized module system that can load libraries directly from github and a JS FFI! See this paper for advanced examples that you can copy-paste to the REPL: http://www.iro.umontreal.ca/~feeley/papers/BelangerFeeleyELS...


It's said that the father of LISP, John McCarthy, lamented the W3C's choice of SGML as the basis for HTML : « An environment where the markup, styling and scripting is all s-expression based would be nice. » The {lambda way} project could be an answer, small and simple, a minimal enhanced text-editor: http://lambdaway.free.fr/lambdawalks/


Every lisp (s-expressions really) fan would say most syntaxes/languages would be nicer if they were expressed in s-expressions instead of the c-like aberration most language copy, myself included. I guess McCarthy was simply the first to express that sentiment :)


Reminds me a lot of Parenscript which we use heavily in Nyxt.

Parenscript: https://cliki.net/Parenscript


I'm not a huge fan of the scheme style programming languages although pretty standard even in Go templates because the parenthesis end up with ugly syntax that becomes difficult when using multiple levels, but Racket does seem cool. I found their guide on building web apps to be pretty amazing and I think that is what HN uses for their website.


Having some more syntax for things like maps and destructuring (like Clojure) goes a long way to remove the worst offenders (((like these))), but as others have mentioned, you get used to it, and then you miss it in other places.

You are working with shapes of data, not lines of text. Using tools like Paredit you can get into an incredible flow akin to that of Vi/Emacs mastery, with the added layer that you are moving entire blocks rather than single words (that may or may not end up being valid code).


> the parenthesis end up with ugly syntax that becomes difficult when using multiple levels

This is really something you will get used to very quickly, and dare I say, learn to appreciate.


> learn to appreciate.

I don't think so. AFAICT, people do hate parenthesis, no matter how long they use Lips/Scheme. They just simply get used to the clutter.

Also, when writing code, you can spam ")" to close functions/expressions. It takes only few brain cycles if the editor highlights matching parenthesis. This makes it easier to move code around. Copy-paste-))))))...


>I don't think so. AFAICT, people do hate parenthesis, no matter how long they use Lips/Scheme. They just simply get used to the clutter.

People see trees and not parens. People who complain about parens are still stuck in the "code is text" mind set rather than the "code is a tree" mind set.

>Also, when writing code, you can spam ")" to close functions/expressions. It takes only few brain cycles if the editor highlights matching parenthesis. This makes it easier to move code around. Copy-paste-))))))...

We've had editors since the 1970s that can take you to where a matching paren is closed. You then copy/paste the whole function without needing to add or remover parens.


> People see trees and not parens.

Codes are still text unless you're using tree-based editors. Maintaining tree on text editors requires careful editing and strict practices, which human brains aren't good at. The "tree" will get broken at some point, but, in this school of thought, the recovery strategy is unclear and inconsistent at best. This becomes annoying pretty quickly.

The real tree, at the end of the day, is formed by line breaks and indents, not parenthesis. Codes are also for human consumption after all. Now there are two trees to manage - semantic indentation and parenthesis - which is bad. Luckily we can induce parenthesis from the semantic structure, so we only care about the semantics, and naturally trailing ")" becomes completely redundant.


>Maintaining tree on text editors requires careful editing and strict practices, which human brains aren't good at. The "tree" will get broken at some point, but, in this school of thought, the recovery strategy is unclear and inconsistent at best.

We've had editors that automatically do that since the 70s. Here's a live demo hot from 1986: https://youtu.be/-J_xL4IGhJA?t=2416

>The real tree, at the end of the day, is formed by line breaks and indents, not parenthesis.

It isn't. You're not the type of person who lisp is for, which is fine. There are lots of languages suited for people like you.

But I'd be curious to know what language you think uses nothing but line breaks and indents to describe its AST.


> Here's a live demo hot from 1986: https://youtu.be/-J_xL4IGhJA?t=2416

The process in the video still perfectly aligns with my description. Actually, I reached that conclusion by observing people writing Lisp code. That is, when you're writing Lisp code, that's exactly what people next you would see, no matter what you think.

Also, trailing ")"s are clearly a burden, as people do spend their brain cycles to properly match them.

> It isn't. You're not the type of person who lisp is for

You sound like you're right in the middle of Lisp fever, which is fine. Everyone goes through that.

> But I'd be curious to know what language you think uses nothing but line breaks and indents to describe its AST.

You simply didn't get what I was saying. Even when all the parens were stripped off from a Lisp code, you can easily reinsert parens based on line breaks and indents. So people rely more on indentations for correctly putting parens, rather than being cautions w/ every single editing operation.


>That is, when you're writing Lisp code, that's exactly what people next you would see, no matter what you think.

People don't match them. The editor does. Computers are very good at this. Code editing isn't a spectator sport where you can judge how easy something is by watching someone else do it. Try par-edit-mode from emacs, it's a tree editor in a text editor. You'd have your mind blown from this piece of technology from the 90s that uses nothing but plain text to edit a tree using parens.

>You simply didn't get what I was saying. Even when all the parens were stripped off from a Lisp code, you can easily reinsert parens based on line breaks and indents. So people rely more on indentations for correctly putting parens, rather than being cautions w/ every single editing operation.

Really?

So given

   f a b
which of

    (f a b)
    (f (a b))
    ((f a) b)
did I strip the parens from?


I think I get picture here that you're probably taking my comment as some old-school Lisp-parenthesis-meme stuff. That's not it. I'm just freaking annoy by that we're using the same shit after decades. Nothing looks cool to me anymore.

> People don't match them. The editor does. Computers are very good at this.

Fixing parenthesis is a semi-automatic operation, performed by people aided by machine. People spend brain cycles on it, still in 2022.

The tree-editing stuffs are in the same line. It's whole purpose is avoid dealing with parens and nested structures, but using it requires in-brain abstraction and getting used to a different set of operations. Extra costs to cognition and operation of the editor. Nothing difficult, but you have other dozens of "nothing difficult" to stack on top of it. Welcome to the world of complexity.

> Try par-edit-mode from emacs

ParEdit is an addition to text editor, and it doesn't block you from introducing mismatched parenthesis. It's actually an important feature, because an editor that forbids this will be more painful to use than manually fixing parens. So we still fix parens in 2022.

Maybe I should compare this to ";" in C-like languages. There are editors, extensions, and formatters that can automatically insert semicolons, but, for whatever reason, semicolons go missing, so people fix it manually, still in 2022. Worse, in Javascript, missing semicolons often causes unintended behaviors, which makes it difficult to debug. Such a nice bug to have in 2022 on a language from 1990s.

Holy Jesus, is this really what we should be dealing with until the last day in our lives? Including all the ancient craps that predates myself? I don't think so.

> did I strip the parens from?

Your example doesn't have any visual structure, and, in practice, we can just refer to the definition of `f` to solve that. Not an actual problem.

What I was talking about is something like this:

    defun find-frame-class name
      cond
        and char= char name 0 #\T
              not member name '"TXX" "TXXX" :test #'string=
         ecase length name
           3 'text-info-frame-v2.2
           4 'text-info-frame-v2.3
        string= name "COM"  'comment-frame-v2.2
        string= name "COMM" 'comment-frame-v2.3
        t
         ecase length name
           3 'generic-frame-v2.2
           4 'generic-frame-v2.3

(The original code is from https://gigamonkeys.com/book/practical-an-id3-parser.html )

The tree structure is visually represented in the indented code, which is far much easier for human brain to process. You almost immediately notice where parens should go.

To recover parens without lines breaks, you should go over each symbol one by one, tracking their contexts. You know, state is evil.

So the information embedded in code format is redundant to parens. IIRC, there were people who tried to abuse this to eliminate parens. Not really sure how that went.


>ParEdit is an addition to text editor, and it doesn't block you from introducing mismatched parenthesis. It's actually an important feature, because an editor that forbids this will be more painful to use than manually fixing parens. So we still fix parens in 2022.

_It does._ This is like trying to explain red to a very obstinate man wearing a blind fold their whole life.

>What I was talking about is something like this:

I have no idea which of:

    (ecase length name)
    (ecase (length name))
    ((ecase length) name)
you want.

What you're complaining about is that operators of arbitrary arity need a termination symbol. That is a feature, not a bug. Basically your code is very simple and you're not using higher order functions. At which point you might as well go back to python.

To actually encode the information you're talking using nothing but white space you'd need to do the following:

find-frame-class

  name
  
  cond
   and
    char=
     char name 0
    #\T
     not
    member
     name
     quote
      "TXX"
       "TXXX"
     :test #'string=
   ecase
    length
     name
     3
      'text-info-frame-v2.2
     4
      'text-info-frame-v2.3
   t
    ecase
     length
      name
     3
      'generic-frame-v2.2
     4
      'generic-frame-v2.3
or some such. I lost interest in getting it correct since I wasted a rather long time trying to fit a tree in a 2d page at university. The key point is that a tree can be infinite dimensional and a page isn't. When you know that you rather quickly realize that all syntactic sugar is doomed and you spend your life doing more productive things with it.


You’re wrong in both points


To be more precise it uses Arc that is implemented on Racket. An example of the programming approach Racket provides (https://beautifulracket.com/appendix/why-lop-why-racket.html).


[flagged]


Because they found it interesting and/or useful. The universe does not revolve around you.


If you spin around in your chair, it kinda does.


[flagged]


We've banned this account. If you don't want to be banned, you're welcome to email hn@ycombinator.com and give us reason to believe that you'll follow the rules in the future. They're here: https://news.ycombinator.com/newsguidelines.html.


I can stil use it


Oh yes, a Racket to JavaScript compiler is on the same level of “interesting” as the Holocaust. Not sure how I missed it before.


"Don't feed egregious comments by replying; flag them instead."

a.k.a. please don't feed the trolls

https://news.ycombinator.com/newsguidelines.html


Sorry. It got the better of me.


There are many examples. But what I'm trying to say is not everything interesting should be done. Maybe there is a legitimate purpose for this. That's what I'm trying to see. But you had to make it personal and attack me. So..


I don't know, a full stack Racket web app sounds fun to me. Why not?


For the clout of course

Check this out, react in clojure

https://reagent-project.github.io/


JS basically is Scheme, after all.



I've never understood how someone who supposedly liked, wanted, and had experience with Scheme ended up creating JavaScript, even being rushed to do so. This isn't a personal judgement. It's just that I don't understand it.


Here is what I don't understand:

How someone who supposedly liked, wanted and had experience with Scheme ended up creating a language with a function-scoped var binding construct, which then took twenty more years to finally get a block-scoped let.


Suits wanted Java because nirmies.


Because customer said so.


Maybe 'that person' was against homoiconic marriage too.


tl;dr a list of random things, then stating that this proves that javascript is not basically a scheme. 4/10, effort appreciated.


If by random you mean things that every scheme share then sure.

It's about as random as requiring mammals to be warm blooded.


With a sparkling of SELF.




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

Search: