Recoil: Rust-inspired C Transpiler with Rebol Syntax

Rebolek has made a bold step forward, bringing us ever-closer to the Rebol vision of a 1:1 users-to-implementation ratio of Rebol-like languages:

http://rblk.eu/recoil/

(Though to be fair, it's more of a Red/System alternative than a "Rebol".)

It's a LOAD-able syntax which is then turned into C code.

Assignment Is Movement, GET-WORD! is "Borrow"

Assigning things moves them.

string! s1: "Hello"
string! s2: s1      ; s1 is now 'moved'
print s1            ; ERROR: Use of 's1' after move

Dialect-wise, he uses GET-WORD! in function specs to indicate "borrowing" without movement. You put the notation on the function argument and also at the callsite:

greet: func [
    :name [string!]
] [
    print name
]

string! msg: "Hello"
greet :msg     ; borrowed -- msg stays alive
print msg      ; valid -- msg was borrowed

He Plans To Use It To Make Synth Modules

He had written a synthesizer in a mix of Rust and C++:

But unhappy with the complexity, he felt that a Rebol-inspired language could do it simpler--in particular on the GUI side.

Red/System didn't meet his needs because it isn't 64-bit. :man_shrugging:

Hello.

Yes, it's not Rebol. It only shares its syntax. I don't target high level, Recoil is low-level by design, even if it's trying to hide the complexity, therefore borrow-checker. Current progress includes basic concurrency with go [thread-code] and port! model based on Rebol. It has modules, first class error handling (currently faked, but it's on roadmap), defer and defer/error, comptime and compfunc for compile time fun, Rebol interoperability in runtime using rebol [rebol-code] that supports passing results back to Recoil with string! result: rebol [rejoin ["hello" space "word"]] and probably some stuff I forgot.
The goal is to have C-level fast dialect of Rebol with less complexity than other languages offer.
If anyone finds it interesting, feel free to ask any questions.

2 Likes

Interesting to see...though of course an indictment of Red/System if it's really not good enough to use after all these years.

(Had the LOAD-able IL been some kind of compatible subset of LLVM--as I had suggested--then things would presumably be different. Because you'd have the choice to install Clang and run it through that.)

Because you're transpiling to C, this actually could be used as an alternative implementation language for user natives:

https://rebol.metaeducation.com/t/no-preprocessing-no-ffi-just-awesome-rebfunction/2224


; Block condition
if [a > b] [
    print "a is greater"
]

As you're using a Rebol parser, any real reason to make that a BLOCK! and not a GROUP! ("paren!"... I forget anyone actually still calls it that...)


void! — No Return Value

The void! type represents "no return value". Functions without a return: spec return void:

log: func [msg [string!]] [
   print msg
]

; log returns void — cannot be assigned
log "hello"

Since you are introducing the term VOID!, I will mention that what you call VOID! (the result of a function with "no interesting result") I would call "TRASH!"... because to me VOID! is a purposely "vanishing" value.

>> 1 + 2 print "Hi"  ; PRINT gives TRASH!, will show nothing in console

>> 1 + 2 ()  ; empty GROUP! makes a VOID!
== 3

>> 1 + 2 comment "hi"  ; comment makes a VOID!
== 3

TRASH! is neither truthy nor falsey--it gives errors in things like ANY and ALL. But you can use ELIDE to evaluate an expression and return VOID!, which is ignored by them.

>> all [1 + 2 print "problem" 3 + 4]
problem
** Error...

>> all [1 + 2 elide print "okay" 3 + 4]
okay
== 7

It's my feeling that this "opting out" is the best application of the term VOID! (the word connotes with "vanishing" or "not even there"). I think it's nice for instance to have things like APPEND be a no-op if passed a VOID!, but error when passed a TRASH!

>> append [a b c] ()
== [a b c]

>> append [a b c] print "then this is an error"
then this is an error
** Error ...
  • if [a > b] [print "a is greater"] this is just a relic of first implementation.Today the condition is an expression, not block! or paren! (it can be paren! as that’s an expression also).
  • void! is absence of value, I chose void! instead of unset! as it’s closer to underlying C ( and shorter :wink: ). trash! is ... interesting choice. IMO if function returns trash, it returns something, even if I may not like it because it’s, well, trash.
  • all, append, etc behaviour - that’s too soon to decide, I’m not that far.

I realize that compiled "system" form, and non-compiled form have different concerns.

But that's why I called out that if in the non-compiled form, if [1 = 2] [print "truthy"] would print "truthy"... that it should be avoided to diverge in the compiled form (especially when simply restricting the expression to if (1 = 2) [print "truthy"] would yield a compatible response.

Certainly it won't align perfectly but I feel one should only deviate when deviation is needed.

In this way, I think that if your "systems-level" language has a PRINT, it should try to match the character of the non-systems-level PRINT.

(You're one of the 2 or 3 people on the planet who may remember that Nenad and I actually argued a bit way back, when he had made Red/System PRINT not add a newline but the Red PRINT did, that "they were two different layers, and the different layers had different understandings". Anyway, I stand by my opinion that the value proposition is that the layers don't confuse you for no reason. It is in this vein that I tell you VOID! as a concept has achieved a certain status in my design, which I would try to keep consistent... even if it's not as obvious at the systems layer.)

1 Like