Op and Pipe-words

And we are finally here. First noticeable thing that probably hit you in the face if you looked at various Rye examples.

Calling functions

Calling functions in Rye is simple. You use a word, that function is bound to, and provide the arguments.

When evaluator gets to a word bound to a function, it goes forward collecting necessary number of values for arguments and then evaluates the function. Here are few simple examples:

print "Call me"
; prints: Call me

inc 100
; returns: 101

concat "Fizz" "Buzz"
; returns: "FizzBuzz"

factor-of 15 3
; returns: 1

replace "Hello World" "World" "Mars"
; returns: "Hello Mars"

Calling functions with op-words

Now we will call the same functions with the same arguments, but we’ll use op-words instead of regular words. Op-words begin with a dot. In case of an op-word, first argument is taken from the left.

"Call me" .print
; prints: Call me

100 .inc
; returns: 101

As we see in next examples, only the first argument is taken from the left. The rest follow like with regular words.

"Fizz" .concat "Buzz"
; returns: "FizzBuzz"

15 .factor-of 3
; returns: 1

"Hello World" .replace "World" "Mars"
; returns: "Hello Mars"

Operators

What are operators then. Operators in Rye are nothing special, they are just regular functions usually taking two arguments. The only difference is that one and two letter operator characters are by default recognized as op-words, they don’t need the dot in front.

The regular spelling of the word in this case is with added underscore in front op-word. For op-word +, regular word is _+.

_+ 3 2                  ; _+ regular word
; returns: 5
_* 3 2
; returns: 6

3 + 2                   ; + op-word
; returns: 5
3 * 2
; returns: 6

Pipe-words

Pipe words behave similarly or in simple cases exactly the same. So you will get the same results in these examples if as we got them with op-words:

"Call me" |print
; prints: Call me

100 |inc
; returns: 101

"Fizz" |concat "Buzz"
; returns: "FizzBuzz"

15 |factor-of 3
; returns: 1

"Hello World" |replace "World" "Mars"
; returns: "Hello Mars"

3 |+ 2
; returns: 5
3 |* 2
; returns: 6

The difference

The difference is in how they combine into larger sentences, how they combine with values, regular words and other op/pipe-words.

Op-word applies to the first value it can on the left, and pipe-word lets all expressions on the left evaluate and then takes the result as the first argument.

This simple example shows the difference:

13 + 14 .print |print
; prints:
; 14
; 27

More examples

inc 10 * 10
; returns: 101

inc 10 |* 10
; returns: 110

"hi " .concat to-upper "joe " .concat "doe"
; returns: "Hi JOE DOE"

"hi " .concat to-upper "joe " |concat "doe"
; returns: "Hi JOE doe"

"Fizz" .print .concat "Buzz" .print |print
; prints:
; Fizz
; Buzz
; FizzBuzz

First argument choice

Because first argument comes from previous expressions on the left, functions in Rye use the active/input value as the first argument if possible.

It’s not always strictly determinable, but in general in a function one value is the input, the active value and other values are often parameters / settings / options. This sometimes means the same order as we’re already used to rom other languages, but sometimes order is specific.

Let’s look at one such chain:

; file hello.txt contains "Hello World"

msg: "hello"

read to-file msg .concat ".txt"
|replace "World" "Mars"
|split " "
|second |printv "Planet {}"
; prints: Planet Mars
|write %planet.txt
; writes "Mars" to file planet.txt

From example above, in replace we expect active value to be the first argument, but printv and write would probably take template string or filename as the first argument in other languages.


– Bonus –

Second argument from the left

In practice it shows it’s sometimes very beneficial if you can take second argument from the left, not the first one. Be it that the value that is coming down the pipe is the second argument, or you want to change the order of arguments to get the desired effect. Or with generic functions, more on that later, they dispatch on the kind of first argument, and sometimes active value is second argument.

So if an op or pipe-word ends with a star “*” at the end it takes the second argument from the left, not the first.

; let's say that the word to replace comes down the pipe, not input string 

word: "apple"

str: "did snake eat the apple?"

word .replace* str "****" |print
; prints: did snake eat the ****?

; or we want a reverse effect

word .concat* "- " |print 
; prints: - apple