Community discussions

MikroTik App
 
GreenDog72
just joined
Topic Author
Posts: 1
Joined: Wed Jul 20, 2022 8:58 pm

A few undocumented operators that are kind of neat.

Fri Jan 13, 2023 6:14 pm

This post will only be of interest to a few select individuals. If you are unfamiliar with mikrotik scripting, or have never used functions, this post is probably not for you, but you're welcome to read it anyway.

I'll look at two operators I found by hitting F1 while in some parentheses.

The first one we'll look at is called quote. It is a unary '>' operator and functions exactly like quote in LISP, for anyone familiar.
It returns whatever it's applied to as a literal. Even code. This means it can be used as an alternate way to make functions.
Here are all three ways I know of to make a function, including this new operator in action:
:global func1 do={:put "test1"}
:global func2 [parse ":put \"test2\""]
:global func3 (>[:put "test3"])

Interestingly, all 3 methods produce slightly different results. Here's what you get when you put all 3 of these
put $func1
# ;(evl (evl /putmessage=test1))
put $func2
# (evl /putmessage=test2)
put $func3
# (evl (evl /putmessage=test3))
func1 is array with 2 elements, the second being a code object. The other 2 are code objects themselves. If you don't know what I mean by this, try using typeof on these.

In practical terms (at least as far as I can tell), these are all treated identically, and it doesn't matter which method you use to produce your functions. Technically there's a very slight performance increase using parse, and it uses slightly less memory for variable storage, because it lacks the additional evl and isn't in an array. These are probably negligible for everything you could want to do with this.

I'm sure I haven't fully explored the power of this operator, for instance, I haven't used it on a string with embedded command substitutions. That could be fun. But I'm moving on.

The next operator I'll talk about is one labelled "activate in environment". I first discovered this one back in June of last year, a coworker made a forum post about it for me, and rextended was also confused, I shelved it. But now I've finally cracked it, and I think it's pretty neat.

The operator looks like this: <%%. It takes a little explaining. Take "environment" in this context to just mean "the collection of variables available". What this operator actually does is run the code object on the left hand side with the array on the right hand side added to it's environment.

As an example, say you make the following function:
:global add (>[:return ($0+$1)])
# This can now be called like this to print 3
:put [$add 1 2]
# To use our new operator with the same result, you do this:
:put ($add <%% {1;2})
Edit: using brackets here doesn't actually work because $0 is the name of the function. It would have to be $1+$2 and the "environment" passed to <%% would be {"add";1;2} for equivalence


If you have a keen eye, you just noticed that the contents of the array were accessed with $0 and $1. The array that was passed in is now the environment (collection of variables) for the function, so the 0th element is accessed with $0 and the 1st element is accessed with $1. This alone isn't particularly powerful, so let's look at another example.
:global f (>[:put $output])
# We have not defined output anywhere. It does not exist. And yet:
($f <%% {output="I exist!"})
# This will add the output variable from the array to the environment, giving the function access to output
# Right now, it's still equivalent to
[$f output="I exist!"]

This is more exciting! We can make objects the encapsulate parameters for a function, and give it that object. This alone may be giving some people some ideas. But if this excites you, I have another example to blow your mind. What if I told you that the array you pass in is passed by reference?
:global inc (>[:set $counter ($counter+1)])
:global c {counter=0}
# What do you suppose happens here?
($inc <%% $c)
# There is no longer an equivalent with bracket notation
# This is NOT equivalent anymore
[$inc $c]

Believe it or not, this works how you think it should. Go ahead and put the value of $c afterwards. You'll find that counter has indeed incremented to 1. We can now pass in an object (array), do something with it's variables, and have those changes stick. This is new functionality unique to this operator. It *almost* feels like object oriented programming, but it's not quite that powerful.

I can understand why Mikrotik hasn't documented this, and I don't expect them to going forward. It's niche, and only a handful of users could even hope to find a helpful use case. I'm not even claiming I have one. But it's another quirky tool to help write some interesting scripts, and I look forward to the possibilities.

Who is online

Users browsing this forum: norepto and 9 guests