abstract sclass Op extends F1 {} sclass ApplySF extends Op { S name; int pos; L args; *() {} *(S *name) { args = ll(); } *(S *name, int *pos, O... args) { this.args = asList(args); } O get(O o) { L args = cloneList(this.args); args.add(pos, o); ret makeAndCall(name, toObjectArray(args)); } } sclass ApplyFunc extends Op { S code; *() {} *(S code) { this.code = trim(code); } O get(O o) { ret evalFuncWithImportExport(code, o); } } sclass MapSF extends Op { Op op; *() {} *(Op *op) {} O get(O o) { ret map(op, (Collection) o); } } sclass Operation { Op op; Data result; *() {} *(Op *op) {} } sclass Data { O data; PersistableThrowable error; new L operations; *() {} *(O *data) {} } static Data initial; svoid printTree(Data data) { if (data == null) { print("-"); ret; } if (data.error != null) print("Error: " + data.error); else { print_noNL("Data: "); if (data.data instanceof Collection) print_noNL("[" + l(data.data) + "] "); else if (data.data instanceof S) { int n = numLines((S) data.data); if (n > 1) print_noNL("[" + n + " lines] "); } print(shorten(struct(data.data))); } print_threadIndentPlus(); try { int i = 0; for (Operation op : data.operations) { print("Op " + (++i) + ": " + struct(op.op)); printTree(op.result); } } finally { print_threadIndentMinus(); } } static Data doOp(Data data, Op theOp) { Operation op = new Operation(theOp); data.operations.add(op); try { op.result = new Data(op.op.get(data.data)); } catch e { op.result = nu(Data, error := persistableThrowable(e)); } ret op.result; }