sclass InputProcessor1 { Map> propertyMakers = ciMap(); class Out > PropertiesAndErrors { S input; bool hasProperty(S name) { ret properties.containsKey(name) || propertyErrors.containsKey(name); } void triggerProperty(S name) { if (hasProperty(name)) ret; O f = propertyMakers.get(name); if (f == null) ret; callFAndPutInResultOrErrorMap(properties, propertyErrors, name, f, input); } O getProperty(S name) { if (properties.containsKey(name)) ret properties.get(name); if (propertyErrors.containsKey(name)) throw throwPersistableThrowable(propertyErrors.get(name)); triggerProperty(name); if (propertyErrors.containsKey(name)) throw throwPersistableThrowable(propertyErrors.get(name)); ret properties.get(name); } } 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 set(new Out, +input); } void addStandardFunctions(S... names) { putAllAsKeyAndValue(propertyMakers, names); } }