Does anybody have any suggestion for writing a retry function if error is thrown?
This works, but it’s not nice. Basically, I would like to use this to catch random curl error from system call and retry it a few times before actually throwing error
enlist2:{$[0>type x;enlist x;x]};/wrap atom in list for calling trap
.e.EXCEPTION:`e;
.e.fsafe:{[f;x] .[f;enlist2 x;.e.EXCEPTION]};
.e.retry:{[f;x;n]
while[(0<n-:1) and .e.EXCEPTION ~ r:.e.fsafe[f;x]];
r};
.e.retry[{x+1};f;3]/return
e after 3 retries
.e.retry[{x+1};1;3]/return 2 with no retry
.e.retry[{system “curl http://www.google.com”};1;3]/ok
.e.retry[{system “curl x”};1;3]/fail after 3 retries
Hey Gab,
I understand the code, mostly. Could you add a few comments in the code?
Sent from my iPhone
Can use .z.s
try:{if[z=0;:`fail];$[1=count value1;@;.][x;y;.z.s[x;y;]{y-1}[;z]@]};
try3:try[;;3];
try10:try[;;10];
Handles any case I’ve tested (though maybe not everything)
/nulladic
q)try3[{0N!“try”;1+1};`]
“try”
2
q)
q)try3[{0N!“try”;1+a};
]
“try”
“try”
“try”
`fail
/monadic
q)try3[{0N!“try”;x+1};1]
“try”
2
q)try3[{0N!“try”;x+1};`a]
“try”
“try”
“try”
`fail
/polyadic
q)try3[{0N!“try”;x+y};(1;1)]
“try”
2
q)try3[{0N!“try”;x+y};(1;`a)]
“try”
“try”
“try”
`fail
/others
q)try3[sum@;(1 2 3;4 5 6)]
5 7 9
q)
q)try3[sum@;(1 2 3;(4;5;`a))]
`fail
q)try3[upper;“abc”]
“ABC”
q)
q)try3[upper;1]
`fail
Terry
Hi Gab,
I’d consider getting rid of the while loop and replacing with something like:
.e.retry:{[f;x;n]$[n&.e.EXCEPTION~r:.e.fsafe[f;x];.z.s[f;x;n-1];r]}
Imposing some sort of sleep command between tries might also be a good idea depending on application.
Thanks Terry. I have learned a lot from your solution!
1. value{x+y}[1] returns arguments
- @ and . can be dynamically evaluated from other execution control statement here
$[1=count value[x]1;@;.]
- One thing I cant figure out. what does @ modifier do in these 2 places?
sum@[x;y;.z.s[x;y;]{y-1}[;z]@]
Thanks Kevin & Terry. Based on Terry and Kevin’s solution. I have decided o go with this (also added comments for Michael)
-
use recursive .z.s instead of while
-
add sleep before retry in fsafe
-
get rid of enlist2 and push the responsibility of ensuring list to the call all together (simple code this way)
.e.EXCEPTION:
fail;/wrap function f with protected evaluation http://code.kx.com/wiki/JB:QforMortals2/execution_control#Protected_Evaluation/if error, sleep for 3 seconds and return fail .e.fsafe:{[f;x] .[f;x;{system 0N!"sleep 3";.e.EXCEPTION}]}; /sleep is not supported on windows/return value from .e.fsafe if not equals to
fail/or recursively call itself until (1)e.fsafe does not return fail or (2)number of retries n is 0/return
fail after n retries if error.e.retry:{[f;x;n]$[n&.e.EXCEPTION~r:.e.fsafe[f;x];.z.s[f;x;n-1];r]}/test code.e.retry[{0N!“try”;x+1};enlist `f;2] /return `fail after 3 retries.e.retry[{0N!“try”;x+1};enlist 1;2] /return 2 with no retry/then i can just define my curl function.u.curl:{.e.retry[{system “curl "”,x,“"”};enlist x;3]};.u.curl[“http://www.google.com”]`
Gab
Hey Gab,
sorry - just seeing this response now!
- Yes, and in fact just using value{x+y} returns even more info…(bytecode;parameters;locals;(context;globals);constants[0];…;constants[n];definition)
-
Correct, and my logic assumes that if function valence = 1 then use @, else use . (which I’m pretty sure holds up in all cases)
-
The @ essentially creates a projection.
q)type[sum]
101h
q)type[sum@]
104h
The difference is subtle. For example:
q)f:floor
q)f2:floor@
q)f 2.3
2
q)f2 2.3
2
However, for function composition, the difference matters
q)g:{x+y}[1;]floor
{x+y}
'type
+
1
_:
q))\
q)
q)g:{x+y}[1;]floor@
q)
q)g 2.3
3
In the first case, the parser was actually trying to add 1 to the floor function which resulted in the error. In the second case, since floor is projected, the end result is that g is a composition which is expecting an input.
q)type[g]
105h
Hope that helps!
Terry
Thanks Terry. Got it. That’s a great example!
Gab