My view is the operators are also functions, so they return values. In the case of [:onerror], it returns a boolean if there was an error (false = no error/do={} run / true = error inside in={}). Since :onerror returns bool if error… that means the return value from the in={} block has no place to go. But this does allow :onerror to be used in a :if statement to test for an “wrapped” error… so that the logic of it I think.
So your :local x; :onerror …; :return $x scheme is likely the solution. Now, avoiding errors by doing type checking/etc is still a better approach in a lot of cases. The main benefit of [:onerror] is you get the error text - which theoretically could be matched/handled further – rather than a script just failing and error in logs (or, blindly skipping all errors (e.g. on-error={}))
Actually :onerror returns value from do={} block if executed when error occurs or false if no error. This is not correctly stated for :onerror command in doc:
…
:onerror can return false (if there is no error) and true (if there is an error) values, so it can be used in :if condition statement scripts.
This quote implies that true value is always returned from :onerror if error occurs which is not true, it’s value from do={} block.
It easy to test with:
> :local fx do={ :local res [:onerror e in={:return (10 / $1) } do={ :put "error executing $0 $1: $e"; :return "test" }]; :return $res }; :put [$fx 0]
error executing $fx 0: division by zero
test
For such reasons :return must be used with some value in do={} block if :error is not called in it (when script is not interrupted by error) because it can cause unpredicted behaviors later in script, see more at http://forum.mikrotik.com/t/conditional-not-boolean-bug-with-onerror-new-in-7-13/172543/19
And as stated by @Ammo above, in={} block has no return value, so variable needs to be declared before :onerror and set in={} block for later usage, even if is just return value of function, so :return with variable value needs to be called outside in={} block as is done in OP 2nd function code.
But I am more included to call it a bug that it :onerror does NOT return “true” when a do={} block is called. In one of the beta/rc (or maybe some stable), I think in={} did require some return value, but that’s not the case any more.
IMO it should just be consistent, either it always returns true/false OR it use the :return value from EITHER do={} or in={} — the half-way house @optio highlights is not helpful since that makes using :onerror with either :if or a function problematic…
I agree for consistent behavior for block returns. Also if block has no :return command, then :onerror is should return :nothing so that :return command is optional in blocks.
I just thought the do={} part was consistent, was my error here. Personally, :nothing be best from both do={} and in={} without an explicit return. Especially, since bool types have their own oddities.
I filed a ticket on this (SUP-174412), since something isn’t right here IMO. With a simplified example:
# corrent per docs, but still weird:
:put [:onerror e in={:return true} do={:return 1.1}]
# false
# wrong! the return value should be true, if docs are right…
:put [:onerror e in={:error "throw"} do={:return 1.1}]
# 1.0.0.1
# correct per docs, since do={} is run
:put [:onerror e in={:error "throw"} do={}]
# true
# wrong, since there still be a return value from :onerror
:put [:onerror e in={:error "throw"} do={:error "here"}]
# here
# also wrong, since return type should be bool
:put [:typeof [:onerror e in={:error "throw"} do={:error "here"}]]
# here
Seemingly the :onerror “eats” the :return statement and the function will work as one that has no :return statement.
In comparison within the :do {} on-error={} statement (and in others like :while, :if) the :return statement works as expected.
Is this on purpose? Kind of weird statement block.
[admin@labISP] > :global f do={:do { :return true } on-error={ :return 1.1}}
[admin@labISP] > :put [$f]
true
[admin@labISP] > :global f do={:onerror e in={ :return true } do={ :return 1.1}}
[admin@labISP] > :put [$f]
;false
[admin@labISP] > :global f do={ :local x "do something"; ("but");("nothing to return") }
[admin@labISP] > :put [$f]
;nothing to return
[admin@labISP] >
This means that a :do {} on-error={} cannot be replaced with :onerror e in={} do={} and this makes updating old scripts much complicated.
While I get idea of :onerror could be used in an :if statement (which docs highlight)…. But in reality it likely be better if :onerror always returned :nothing unless an explicit :return was used in EITHER the do={} or in={} block so :onerror be more convenient in a function (rather than :if) since you can use :onerror to guarantee a return value type from a function.
But at a min here, the docs are wrong.
True. The only downside of :do ... on-error={ ... } is the reason for error is lost, but a lot of case that may not matter and function params likely suggest what the error would be.
Now IMO it's better to protect against failure BEFORE getting to any on-error/[:onerror]. Not saying remove the error protection blocks... more if there are known cases where an error could happen, it be better to use an :if with :typeof or other checks on values to prevent it going to the on-error={} or [:onerror ... do={}] blocks in the first place.