Skip to content

Commit

Permalink
Update unorthodoxy.md
Browse files Browse the repository at this point in the history
  • Loading branch information
refaktor authored Dec 23, 2023
1 parent 73f62e3 commit 6290214
Showing 1 changed file with 67 additions and 12 deletions.
79 changes: 67 additions & 12 deletions _sandbox/unorthodoxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,30 @@

_Being unorthodox offers no benefit by itself._

_If you aren't creating anything really new, just use the old._
_If you aren't creating anything really new, just use the existing._

_**work in progress document**_

Rye is a programming language, no different than _insert-your-own_. Here I will try to document sides of Rye that are somewhat different. Because the things that aren't, you already understand.
Many of these specifics come from Rye's inspirators, Rebol, Factor, Shell and Go. Some are unique.
Rye is a programming language, not that different than _insert-your-own_. Here, I will try to document sides of Rye that are somewhat different. Because the things that aren't, you already understand.
Some of these specifics come from Rye's inspirators, Rebol, Factor, Shell and Go. Some are unique.

## Newlines and spaces

Whole Rye program could be written in one line. There is no concept of lines, no line separators.
```red
print 1 + 100 + 1000 prns "Hello" if 1 { loop 3 { print "World" } } print "Bye"
```

Each Rye token must be separated by a space. This also means that parenthesis or operators must always be one space removed from it's neighbouring tokens.
```red
print 123+234 ; loader error
if 1 {print "OK"} ; loader error
```

Rye knows a token "," called expression guard, which is optional. It can help you explicitly safeguard the limits of expressions or visually signify them.
Rye knows a token "," called expression guards. They are completely **optional**. You can use them so explicitly mark expression boundaries, if you want to.
```red
print 1 + 100 + 1000 , prns "Hello" , if 1 { loop 3 { print "World" } } , print "Bye"
```

## Conventions around nouns, verbs and adjectives ...

Expand All @@ -34,23 +44,62 @@ More conventions
* If a function performs a same task as another, but in a different way or with a different (number of) arguments it can be defined with **\variation** at the end (print\val, load\csv, map\pos)
* If a function ends with **\\**, this means **"more"**, usually means a this variation accepts additonal argument compared to base function (ls\, produce\)
* If a functions changes values **in-place** it has ! at the end (inc! , append! , unique!)


## Functions as Words, Op-words and Pipe-words
## All the word types!?

Rye words that are bound to functions (user or builtin) can be called as words in normal reverse polish notation.
Words, set-words, lset-words, get-words, tag-words, ... why ...

## Assignment, right, left, inline ...

```red
name: "Jim"
20 :age
....
```

## Functions as Words, Op-words, Pipe-words and an odd Star

The same function can be called as an op-word (with a . infront), in this case it takes it's first argument from the left. Pipe word does the same.
Rye words that are bound to functions (user or builtin) can be called as words in normal reverse polish notation.
```red
print "Hello"
print capitalize "jane"
print concat "Am" "mo"
```
The same function can be called as an op-word (with a . infront), in this case it takes it's first argument from the left.
```red
"Hello" .print
"jane" .capitalize .print
print "Am" .concat "mo"
```

Difference is in priority. Opwords take the first argument on the left they can find, pipewords evaluate all expressions on the left and take the result as argument.
```red
"Hello" |print
"jane" |capitalize |print
"Am" .concat "mo" .print ; will print mo
"Am" .concat "mo" |print
```

Operators are implicitly op-words, without the need to add them .. Their spelling is _+ _*. They can be called as pipe words too.
```red
1 + 2
23 + 33 * 32 / 2
23 + 33 * 32 |/ 2
_+ 23 43
```

## Returning words

Rye doesn't use return statement. It always returns the last value (akin to rebol, lisp, ...). You can use return function to return before that. But also other functions can return or conditionaly return. Because we want them
to be obviously visible, convention is that they should have ^ at the begining. Such words are ^if, and some of the failure handling functions ^fix ^check

```red
check: fn { name } { ^if name = "foe" { "go away" } "hello" }
check "friend" ; hello
check "foe" ; go away
```

## Failures and Errors

Programmer's errors (bugs) and failures are not the same. Failure is a (not unexpected) result of a function that couldn't do (return) what it is expected to do.
Expand Down Expand Up @@ -94,14 +143,20 @@ I am not expert on Lisps, not even Rebol, I am just speaking my mind. You are we
Rebol has dialects, Lisps have macros. Each of them have benefits, but they also incur a cost, as they are an exception(1).
An optimisation in syntax, elegance, reduction in code, but still an exception. Exception you have to **know** **about**, **notice**, **learn**, **understand** (3) ...

In lisps, **macros** can be casually mixed with non-macro words (functions). Well, they by default usually are, since some of the basic construct of a language are macros. *if* (2), *defn*, *loop* are usually macros.
Macros can change the rules of the game. So you have this maximaly uniform language _where everything is a list_, but any token inside this language could change the rules. That's why many Lisp authors warn about macro overuse.
In lisps, **macros** can be casually mixed with non-macro words (functions). Well, they by default usually are, since as macros alongside functions form many foundational constructs. *if* (2), *defn*, *loop* are usually macros.
Macros can change the rules of the game. So you have this maximaly uniform language where code and data are uniformly represented as lists, but any token inside this language could change the rules. That's why many Lisp authors
warn about macro overuse.

```
Guy Stelle (of Scheme) said: "Macros are a powerful tool, yet they should be used sparingly. Overuse of macros makes programs hard to read, hard to debug, and hard to maintain."
Doug Hoyte (of Let over Lambda): "Macros should be invisible to the user or be more beautiful than the alternative."
```

My _naive idiot's_ opinion is, that macros in lisps (as these powerful but potentialy dangerous constructs) maybe have suboptimal distribution/visibility.

I dare not to really compare Rebol to the great family of languages as Lisps are (now I made both Rebolers and Lispers mad). But Rebol's approach avoids some of the "troubles?" lisps have. Some say
Rebol is like a Lisp without parenthesis, but we don't care about that here. Rebol's blocks (lists) DON'T evaluate by default. This miniscule detail changes a lot. You don't need any special forms, because functions can accept
I dare not to really compare Rebol to the great family of languages as Lisps are (now I made both Rebolers and Lispers mad). But Rebol's approach avoids some of the troubles lisps have. Some say
Rebol is like a Lisp without parenthesis, but we don't care about that here. In Rebol, blocks (akin to lists) do not auto-evaluate, a significant departure from Lisp's default behavior. This miniscule detail
changes a lot. You don't need any special forms, because functions can accept
blocks of code directly (since they don't get evaluated by runtime) ... so *if*, *defn*, *loop*, etc are just functions. Functions like any other functions. So Rebol doesn't have or _need_ macros.
But it has **dialects**. It has really interesting approach to dialects, with it's parse _dialect_ (I know :P). Dialects are like special interpreters for Rebol tokens. They have better separation (a benefint in clarity, a cost in reuse)
than macros, they always exist in their own blocks. They aren't sprinkeled aroung your regular Rebol code, but again ... the more you use them the more full of specail cases (syntaxes, evaluation rules) your code becomes.
Expand Down

0 comments on commit 6290214

Please sign in to comment.