code: private TermType getCastTypeFromSubRule(
              ImmutableTerm term,
              ImmutableList<Function> bodyDataAtoms,
              Map<Predicate, ImmutableList<TermType>> alreadyKnownCastTypes) {
  
          if (term instanceof Variable) {
              Variable variable = (Variable) term;
  
              for (Function bodyDataAtom : bodyDataAtoms) {
  
                  List<Term> arguments = bodyDataAtom.getTerms();
                  for (int i = 0; i < arguments.size(); i++) {
                      /**
                       * Finds the position of the variable in the current body atom
                       */
                      if (arguments.get(i).equals(variable)) {
  
                          // i is not final...
                          final int index = i;
  
                          return Optional.ofNullable(alreadyKnownCastTypes.get(bodyDataAtom.getFunctionSymbol()))
                                  .map(types -> types.get(index)).orElseThrow(() -> new IllegalStateException("No type could be inferred for " + term));
  
                      }
                  }
              }
  
              throw new IllegalStateException("Unbounded variable: " + variable);
          }
          else if (term instanceof ImmutableExpression) {
              ImmutableExpression expression = (ImmutableExpression) term;
              ImmutableList<Optional<TermType>> argumentTypes = expression.getTerms().stream()
                      .map(t -> getCastTypeFromSubRule(t, bodyDataAtoms, alreadyKnownCastTypes))
                      .map(Optional::of)
                      .collect(ImmutableCollectors.toList());
  
              return expression.getOptionalTermType(argumentTypes)
                      .orElseThrow(() -> new IllegalStateException("No type could be inferred for " + term));
          }
          else if (term instanceof Constant) {
              return ((Constant) term).getType();
          }
          else if (term instanceof ImmutableFunctionalTerm) {
              Predicate functionSymbol = ((ImmutableFunctionalTerm) term).getFunctionSymbol();
              if (functionSymbol instanceof DatatypePredicate)
                  return functionSymbol.getExpectedBaseType(0);
          }
  
          throw new IllegalStateException("Could not determine the type of " + term);
      }
docs: Extracts the cast type of one projected variable
  from the body atom that provides it.