Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This took me a minute to get, even after looking at the page about if:

https://ryelang.org/meet_rye/basics/if_either/

The tricky thing about if, and, and or --- the reason you can't implement them as functions in most languages --- is that they need to not evaluate all their arguments immediately. Otherwise:

    // Would print!
    if(false, print("oops!"))

    // Would throw  an error if the key is not present
    and(my_hashmap.has_key("key"), my_hashmap["key"])
The way that ryelang gets around this is that you pass the arguments in a "code block" surrounded by "{}", which delays its evaluation. So you write:

    // Does not print, because *if* never runs its code block arg
    if 0 { print("oops!") }

    // There's no example of *and* anywhere but my guess is you'd write this:
    and { my_hashmap.has_key("key") } { my_hashmap["key"] }


This is a very natural technique in concatenative languages.


> The way that ryelang gets around this is that you pass the arguments in a "code block" surrounded by "{}"

Like Lisp’s QUOTE which has to be a special form.


In the maxima computer algebra system[1] which was ancestrally based on lisp it has a single quote operator[2] which delays evaluation of something and a "double quote" (which acually two single quotes rather than an actual double quote) operator[3] which asks maxima to evaluate some expression immediately rather than leaving it in symbolic form.[4]

[1] https://maxima.sourceforge.io/

[2] https://feb.kuleuven.be/public/u0003131/WBT23/wxMaxima/wxM_i...

[3] https://feb.kuleuven.be/public/u0003131/WBT23/wxMaxima/wxM_i...

[4] although (in my limited experience) maxima sometimes refuses to evaluate anyway even if you do the double quote thing


Is this correct though? Lisp's quote would need some eval or something to evaluate later afaik. More fitting might be a (lambda () ...), a.k.a. lazy evaluation.


The implementation of the if() function would be the one that calls eval() on the true or false code block.


Lambda is a special form


I would imagine it is closer to lambda than quote (though also a special form), since the implementation of if would require that the bindings in the arguments evaluate to their values in the callers environment.


As done in Smalltalk.


Smalltalk may be influential, but is now rarely used.

The code block approach is widely applied in two massively used industrial languages though: Ruby and Kotlin. In Kotlin specifically it's one of the very central features.


And Ruby has been strongly influenced by Smalltalk.

Anyway, if you want something perhaps a bit more used, you can check out Pharo. I think that is the most used Smalltalk-like language these days.


The R language has this feature as well. It's a whole lot of fun to work with.


Likewise in Tcl where blocks can either be sent quoted (using {}) or unquoted (using “”). In the latter, variables and procs will have a round of substitution performed before being passed to the proc.


Rebol (where Rye took this from) is many times associated with Tcl. I've heard good things about it, but haven't really tried it yet.


TCL is kinda similar to Rebol in some ways but in other ways it's the opposite of Rebol, because in TCL everything is a string (although it can ALSO have another type, thanks to clever shenanigans). (You probably knew this!)


I heard this "everything is a string" line many times abot Tcl and it sounded a little unusual, but I havent delved deep enogh in tcl to see what it really meant and brought. I will.


everything has a string rep available. It used to be that every thing was also represented literally by a string. So, for pedagogical purposes, a value 1 would be "1", and to do math, Tcl would do a strtol(val_storage), with the obvious performance implications.

The way things are done now (and have been for a long time), is that values are stored in a

    struct Tcl_Obj{
      int refCount; // objs can be shared
      int myType; // indicates whether currently a long, double, etc
      long longVal;
      double dblVal;
      [...]
      char *stringRep;
      int len;
    }
...in fact, the Tcl_Obj is more sophisticated than this, but for demonstration purposes this is fine.

So "native" (eg: longVal) values are used when appropriate, no marshalling back/forth between strings, but the string rep is always available (can be generated), because that's what Tcl promises: everything is representable as a string. This is what brings the homoiconicity to Tcl - logically it's just passing text tokens around, and emitting text tokens. Internally, again, more sophisticated, but you get the point.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

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

Search: