docs: def apply(self, state_func=None, stash_func=None, stash='active', to_stash=None):
          """
          Applies a given function to a given stash.
  
          :param state_func:  A function to apply to every state. Should take a state and return a state.
                              The returned state will take the place of the old state. If the function
                              *doesn't* return a state, the old state will be used. If the function returns
                              a list of states, they will replace the original states.
          :param stash_func:  A function to apply to the whole stash. Should take a list of states and
                              return a list of states. The resulting list will replace the stash.
                              If both state_func and stash_func are provided state_func is applied first,
                              then stash_func is applied on the results.
          :param stash:       A stash to work with.
          :param to_stash:    If specified, this stash will be used to store the resulting states instead.
  
          :returns:           The simulation manager, for chaining.
          :rtype:             SimulationManager
          """
          to_stash = to_stash or stash
  
          def _stash_splitter(states):
              keep, split = [], []
              if state_func is not None:
                  for s in states:
                      ns = state_func(s)
                      if isinstance(ns, SimState):
                          split.append(ns)
                      elif isinstance(ns, (list, tuple, set)):
                          split.extend(ns)
                      else:
                          split.append(s)
              if stash_func is not None:
                  split = stash_func(states)
              if to_stash is not stash:
                  keep = states
              return keep, split
  
          return self.split(_stash_splitter, from_stash=stash, to_stash=to_stash)