finally found a blog post about rebol/red that com...
# thinking-together
g
finally found a blog post about rebol/red that communicates some of what I suspected was interesting about it: http://www.codeconscious.com/rebol/articles/rebol-concepts.html . in particular, in the screencap: the body of a function and its argument list are just data structures. so you can use them as first-class citzens as you like. please let me know if any of you know of any other languages with this level of flexibility!
bizarrely, some of rebol’s choices about mutability extend to the source code:
r
Lisp is the benevolent grandparent of this concept. But this aspect of Red / Rebol reminds me more of concatenative languages and stack languages like Forth. My hobby language of choice is Nim. Nim is a much more traditional programming language in many ways. It exposes the "code as data" / macro features by essentially providing you with the same AST data structures that the compiler itself uses. Macros in Nim are "compiler plugins" in a very concrete way. This has it's own pros and cons and gotchas. Not many "big syntax", non-homoiconic languages are able to do this. Rebol/Red is one of the few in this category that can. Ruby and Smalltalk arguably achieve similar things, but in a different way. They push more semantics into the runtime via OOP and message passing.
g
yeah—this is a step beyond lisp (and by extension ruby, and maaybe smalltalk)
r
Can you elaborate on what you mean by "a step beyond lisp"? It's pretty hard to beat lisp on flexibility lol. If you look at lisp reader macros, and Racket with it's many DSL's philosophy, I think you will see the same level of flexibility, just in a different form. IMO this is not a case in which one solution is strictly better or more powerful than another. I have a hunch that many macro, DSL generation, "code as data", and "programmable programming language" ideas are isomorphic to each other and are fundamentally equivalent. It comes down to taste, and which solution you prefer on a practical level. I think this is an example of the phrase "there are many ways to cook an egg..." (replace with equivalent phrase from your childhood here 🙂 ).
k
It's beyond Lisp because arguments are received unevaluated, and different operations may parse their args in very different ways. More powerful than reader macros. Also, there's a principled way for macros to track the scope of variables. I'm not experienced in it so don't have a sense for what the cons (ha!) are.
🤣 1
r
"It's beyond Lisp because arguments are received unevaluated, and different operations may parse their args in very different ways." Can you explain that more? I have to admit I have limited experience with reader macros myself. What you are describing sounds equivalent to reader macros to me.
k
Reader macros have to follow certain rules Red doesn't. I don't remember the details, but Red allows special syntax for urls and html I believe. You can't do that with reader macros.
r
The link in my first post uses as an example a reader macro for embedding a full JSON object directly into a lisp program. And Racket Scribble is like a fully embedded markdown parser that can freely mix with lisp code to create a "literate code" experience.
k
I'm not an expert on reader macros, and I wasn't including Scribble. Feel free to dig into Red and show me how I'm wrong 🙂
r
lol. Fair enough. I'm not trying to attack your premise. I'm legitimately curious what the differences are now. I find macros and DSL's endlessly fascinating 🙂 . I'm now googling for more details, though I'm not sure if it will bear any fruit.
k
No reason to be skeptical. I'm sure this has been discussed many times out there. I haven't seen anyone say Lisp reader macros are equivalent. Ah, found the link about how Red tracks variable scope in macros: https://stackoverflow.com/questions/21964110/is-there-a-overall-explanation-about-definitional-scoping-in-rebol-and-red
👍 3
e
Red is the rebirth of Rebol. Nenad is running the project. They have funding (done via a Crypto coin offering), and they have very active community on Gitter. They don't visit here at FoC at all. I occasionally pop in and kibitz. Anyway Rebol is effectively a language where each function is its own domain specific language. It is fairly jarring to those coming from Algol family languages. They use colon (:) for the equal sign, and parameters to functions, which they call refinements, are prefaced with a slash /. They use a clever feature to distinguish type name spaces, a type name has an exclamation point afterwards like rect!, and then you can tell if a name is a variable or is it a type name. I've considered imitating this feature, it solves a lot of namespace collision issues. There are various libraries which go way beyond what a Python library does for example, because it is a whole domain specific language. Red can be justly proud of its "Parse" module, which approaches the pattern matching power of Icon, the king of string manipulators. The Red team is focusing on crypto contracts, but it has a GUI library that can make interesting things. Any dynamic language like LISP or Red abounds with user created custom languages, and learning the subtleties of those languages is not easy. I personally find that the dynamic languages create a memory burden on the reader, as the reader must have in their mind the domain specific language. For very bright people, these languages are attractive, but the high leverage achieved in these languages means that small changes can have drastic effects. Leverage works both ways. Instead of trying to create ever more powerful stacks of function by long sequences of domain specific languages or function composition, i have tried in my Beads language to create compartmentalization, where a section of code's possible effects are constantly being narrowed, so that a mistake in a module can only have a local effect, and thus the overall system is more robust. CSS is an example of a language i consider maddening, because an extra or missing character can cause a baffling scrambling of a page's layout. This type of fragility is not friendly to maintenance. PHP has a similar fragility to it, because it is typically generating HTML/CSS code, and thus doubly fragile. For this reason PHP is losing its luster.
🤔 2
☝️ 2
k
Instead of trying to create ever more powerful stacks of function by long sequences of domain specific languages or function composition, i have tried in my Beads language to create compartmentalization, where a section of code's possible effects are constantly being narrowed, so that a mistake in a module can only have a local effect, and thus the overall system is more robust.
Nice!
r
I personally find that the dynamic languages create a memory burden on the reader, as the reader must have in their mind the domain specific language. For very bright people, these languages are attractive, but the high leverage achieved in these languages means that small changes can have drastic effects. Leverage works both ways.
This is a really good point. DSL's and Macros are a double edge sword for exactly this reason. I think it has been a big contributor to lisps downfall. It's something I personally struggle with reconciling.
Beads language to create compartmentalization, where a section of code's possible effects are constantly being narrowed, so that a mistake in a module can only have a local effect, and thus the overall system is more robust.
I'm curious about how you achieve this. It reminds me of this research project, AbleC, in which they try to create a C compiler that supports extensions / plugins that could be proven to have only local effects in much the same way.
💡 2
@Kartik Agaram That Stack Overflow article about scoping was very enlightening. Thank you! It makes Red remind me even more of Forth now! That's the only other language I can think of that works in a similar way.
d
It's beyond Lisp because arguments are received unevaluated, and different operations may parse their args in very different ways.
So it's the same thing as FEXPRs in Lisp. Not really beyond Lisp, because the idea was invented by Lisp first. https://en.wikipedia.org/wiki/Fexpr
👎 2
👍 1
Smalltalk-72 had something similar: an object could dynamically parse the token stream as a sequence of unevaluated arguments, and that is how message passing worked. It was incredibly powerful, but the idea was abandoned in Smalltalk-76 for a syntax that is more static and more amenable to compilation. The real issue for Smalltalk was that it was difficult for humans to predict how a program would be parsed, and the API for parsing the message stream was more complex than what Smalltalk uses today for specifying message patterns.
👍 1
g
when I was referencing lisp, I meant modern and popular lisps from today—I think rebol’s model is a lot like fexprs but they seem to have a bunch of different handles for dealing with that situation. and they seem to use fexpr-equivalent stuff about a thousand times more often than older lisp programs did. I said “maybe smalltalk” because I was specifically thinking about that thing about every object carrying its own parser. i think rebol gets around some of the lack of clarity by in general writing very small programs (I’m not an expert) and keeping rules for how to treat unevaluated data very close to where you use those rules
forth is super similar, and I think factor’s compilation model leverages some very similar stuff
👍 1
as far as the extra tooling for fexpr-like stuff—red/rebol seem to have a bunch of quote-like facilities for dealing with different stages of evaluation etc. like a
set-word:
vs a
'lit-word
vs a
:get-word
vs a
word
. havent figured out how all of it works together yet, but it seems like it uses the same data structure in pretty much every case… almost like clojure’s omnipresent use of nested maps/edn—but it seems to extend to binding syntax instead of just raw data. idk. I’m finding it pretty mind-bending
s
d
There is also this Hacker News discussion: https://news.ycombinator.com/item?id=11587952
1
r
Lol. I've been schooled. I was not familiar with this particular corner of comparative PL analysis. 🤣 Thank you all for the reading material. 🙏
k
As someone who's built an fexpr-based Lisp: no, Red is not like fexprs. Y'all know I like Lisp, but we lispers need to beware of treating other models as equivalent just because we can do what they do as long as we stick to s-expressions. Even reader macros (which are quite unlispy) don't get you out of jail free from the restrictions of s-expressions. The correct response from a lisper on conversations of syntax is to pooh-pooh it: "syntax doesn't matter, dontcha know?" 😉
❤️ 2
a
I'm sure I could google this, but what's the debugging experience like in rebol? It seems like the very flexible syntax you're describing would make visualizing debug information kind of tricky.
👍 1
g
red and rebol are both actually extremely difficult to google, and the documentation is either scattered or written directly to new programmers or at people who fully grok the language (the community is similar to that of APL in this regard). from what I can tell, debugging relies very heavily on the same reflection capabilities—they have some specialized words i’m not fluent in, like
probe
,
mold
,
bind
, and
BIND?
. I imagine that if you’ve fully learned to read the matrix, it can be really pleasant to hook into code and inspect what’s bound where, evaluate with one function and compare it to another. Like if you had wrenches that fit all the nuts and bolts on your step-through debugger—but it also seems like you have to build the debugger yourself from the kit. I’m no expert, though. I hope (wish?) we have some people with more experience with red and/or rebol specifically in the slack
sidenote: APL also has a really weird scoping model that I don’t fully understand but that’s alluded to during a very fast-paced breakdown of the defn compiler (available on youtube. I can find the link if anyone wants it)
e
I should also point out that there are 2 main dialects of Red, Red/System is a special limited version that is intended for raw metal. It is intended for systems programming applications. Red has one big advantage is that the entire toolchain is around 1MB. So it is like FORTH in that it is super small. A clever person can get a lot out of a language like Red.
k
A comment on the "memory burden" issue with DSLs and related methods: it's all a matter of scale. Plenty of research papers in mathematics and physics start by introducing some new notation, which is the moral equivalent of a DSL. As long as it's a bit per paper, with no idea of universality or reusability behind it, that works very well. It starts getting problematic when someone starts with "using the notation introduced by X in referende Y ...". If you have to read another paper first just to parse the notation, it becomes a chore. So perhaps a good solution is PSL (problem-specific language) rather than DSL. A very local scope for the language. In Lisp terms, one or two short macros that readers are expected to read and understand, rather than a complex language extension packaged as a library (which is what Racket advocates).
❤️ 1
💯 1
y
I was always intrigued by Red's promise of being "truly full stack". I wonder how it works out in practice. Specifically, the lower level is a DSL over Red itself, so what does it feel like to cross the boundary between levels?
❤️ 1
k
Good question... another one: is there a good reason to choose two levels with an explicit boundary in between them? Why not just one (as e.g. on a Lisp machine)? Why not more than two? In other words, is two some kind of optimum, or just a historical accident?
👍 1
d
As someone who's built an fexpr-based Lisp: no, Red is not like fexprs.
The original post was about Rebol, and I was talking about Rebol, not Red. Rebol and Red are different. There are two kinds of Lisp dialect. • In the Macro dialects (modern compiled dialects like Common Lisp and Scheme), there are no FEXPRs. Instead, there are Macros, which receive their arguments as unevaluated syntax objects. There are two phases of evaluation. Calls to macros are expanded at compile time, not at run time. Macros are not first class values. • In the FEXPR dialects, you have FEXPR objects, which are like functions, except that the arguments are unevaluated syntax objects. FEXPRs are first class values. There is only one phase of evaluation: calls to FEXPRs are evaluated at run time. These dialects are interpreted, not compiled. Older examples are Lisp 1.5 and 3Lisp (these are historical dialects, not used today). A newer example is the Kernel language, a dialect of Scheme with FEXPRs instead of Macros, with multiple implementations on github: http://web.cs.wpi.edu/~jshutt/kernel.html Everything I said about FEXPR Lisp dialects seems to be also true of Rebol, and everything I said about Macro Lisp dialects seems to be also true of Red. So that is how I am categorizing Rebol and Red, pending more information. This page from the Red language site seems to confirm what I just wrote: https://www.red-lang.org/2016/12/entering-world-of-macros.html
Good question... another one: is there a good reason to choose two levels with an explicit boundary in between them? Why not just one (as e.g. on a Lisp machine)? Why not more than two? In other words, is two some kind of optimum, or just a historical accident?
I don't know enough about Red to answer this in the Red context, but I can talk about my own experience. My Curv language has exactly 2 levels because Curv can be either interpreted on a CPU, or it can be compiled into GPU shader code and run on a GPU. The run time requirements for these two execution environments are quite different. On the GPU, I can't have a garbage collector, or first class function values, or general run-time polymorphism, whereas on the CPU, I can. So any Curv code that runs on the GPU is restricted to a statically typed subset of Curv. This statically typed subset is upward compatible with the full language, and GPU code can be executed on the CPU without any ceremony, so it feels like you are programming in a single language. The Red language is claimed to be suitable for writing operating systems. You don't want to have a garbage-collected runtime environment inside an operating system kernel, because garbage collection pauses are incompatible with real time programming. For example, you need to be able process interrupts with guaranteed latency bounds. From Red-lang.org, "*Red/System* is a C level language, where you are responsible for allocating and freeing memory, and where you may need precise manual control over those actions." I know that the Lisp machine operating system was written in Lisp. I don't know the details, but it seems plausible that they would have used a statically typed Lisp subset for low-level OS code (device drivers and such), which if true, would put Lisp machine Lisp into the same category as Curv and Red (two levels, including a low-level systems programming dialect).
k
If the system language were a strict subset, I wouldn't count it as distinct. In Lisp, for example, you can use type declarations as much as you like. A subset that requires them everywhere would be more of a style of using Lisp than a dialect, from my point of view at least. I do wonder how Lisp machines got around garbage collection at the lowest level though. In the Lisps I know, there is no opt-out.
k
@Doug Moen, what "original post" are you referring to? Was it outside this thread? You're right that I'd been clumping Rebol and Red in my mind. After your comment I went back and reread http://blog.hostilefork.com/rebol-vs-lisp-macros, and I still want to push back on your statement that "Everything I said about FEXPR Lisp dialects seems to be also true of Rebol." The notion of definitional scoping turns out to also exist in Rebol, and it has a huge impact on how Rebol operations evaluate their arguments. It's a very fruitful exercise to compare and contrast different programming models. Saying, "A is just like B" flattens a lot of the richness, I think. Even if an idea was "invented by Lisp first," there's still been lots of parallel evolution that has converged in surprising ways. Rebol is worth framing as its own thing, on the same order as Forth. It's not a Lisp derivative.
❤️ 2
w
@Kartik Agaram I thought the correct response from a lisper on conversations of syntax is, "You get used to it, I don't even see the code. All I see is blond, brunette, redhead."
😄 1