sclass InputProcessor1 { Map> propertyMakers = ciMap(); class Out > PropertiesAndErrors { S input; bool hasProperty(S name) { ret properties.containsKey(name) || containsKey(propertyErrors, name); } void triggerProperty(S name) { if (hasProperty(name)) ret; O f = 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 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); } }