Add better support for customization of runners
We have several examples of adaptive
usage where people need some functionality that sits between the Learner and the Runner.
Examples are
-
DataSaver
: The learned function returns a bunch of information, and we want to learn only a part of this information (e.g. adict
is returned, and we want only to learn the values with keyresult
), but also keep the full output, for future analysis -
Timer
: We want to time the execution of the learned function -
Cache
: We want to cache some information produced by function calls, to use it in other function calls
The fact that !71 (merged) is necessary indicates that we need to think about the way that this 'middleware' is implemented. It may be that making (semi-)transparent wrappers for Learners is the right design, but we should make it clear that this is what we are doing, in this case.
At the moment runners execute (more or less) the following in a loops: x, _ = ask(); tell(x, f(x))
The idea with a middleware is that you'd place a pair of functions, g
and h
, in between f
: x, _ = ask(); tell(x, g(f(h(x)))
. The question would then be what the exact signatures of g
and h
need to be (e.g. in the previous example g
is not passed enough information to be able to associate the value it receives with a corresponding x
value), and whether they are better modelled by a class, a generator, or whatever.