code: @Override
      public ImmutableSubstitution<ImmutableTerm> composeWith(ImmutableSubstitution<? extends ImmutableTerm> f) {
          if (isEmpty()) {
              return (ImmutableSubstitution<ImmutableTerm>)f;
          }
          if (f.isEmpty()) {
              return (ImmutableSubstitution<ImmutableTerm>)this;
          }
  
          Map<Variable, ImmutableTerm> substitutionMap = new HashMap<>();
  
          /**
           * For all variables in the domain of f
           */
  
          for (Map.Entry<Variable, ? extends ImmutableTerm> gEntry :  f.getImmutableMap().entrySet()) {
              substitutionMap.put(gEntry.getKey(), apply(gEntry.getValue()));
          }
  
          /**
           * For the other variables (in the local domain but not in f)
           */
          for (Map.Entry<Variable, ? extends ImmutableTerm> localEntry :  getImmutableMap().entrySet()) {
              Variable localVariable = localEntry.getKey();
  
              if (substitutionMap.containsKey(localVariable))
                  continue;
  
              substitutionMap.put(localVariable, localEntry.getValue());
          }
  
          return substitutionFactory.getSubstitution(
                  substitutionMap.entrySet().stream()
                          // Clean out entries like t/t
                          .filter(entry -> !entry.getKey().equals(entry.getValue()))
                          .collect(ImmutableCollectors.toMap()));
      }
docs: " "this o f"
  
  Equivalent to the function x -> this.apply(f.apply(x))
  
  Follows the formal definition of a the composition of two substitutions.