sclass InputProcessor1 { Map> propertyMakers = ciMap(); sclass Out > PropertiesAndErrors { InputProcessor1 inputProcessor; S input; bool hasProperty(S name) { ret properties.containsKey(name) || containsKey(propertyErrors, name); } void triggerProperty(S name) { if (inputProcessor == null || hasProperty(name)) ret; O f = inputProcessor.propertyMakers.get(name); if (f == null) ret; propertiesAndErrors_callF(this, name, f, input); } O getProperty(S name) { if (properties.containsKey(name)) ret properties.get(name); if (containsKey(propertyErrors, name)) throw throwPersistableThrowable(mapGet(propertyErrors, name)); triggerProperty(name); if (containsKey(propertyErrors, name)) throw throwPersistableThrowable(mapGet(propertyErrors, name)); ret properties.get(name); } Out detach() { inputProcessor = null; this; } } Out process(S input) { Out out = dontProcess(input); for (S name : keys(propertyMakers)) out.triggerProperty(name); ret out; } // you can query properties on demand Out dontProcess(S input) { ret nu Out(inputProcessor := this, +input); } void addStandardFunctions(S... names) { putAllAsKeyAndValue(propertyMakers, names); } }