code: def get_nonterminal_productions(self) -> Dict[str, List[str]]:
          
          if not self._nonterminal_productions:
              actions: Dict[str, Set[str]] = defaultdict(set)
              # If you didn't give us a set of valid start types, we'll assume all types we know
              # about (including functional types) are valid start types.
              if self._start_types:
                  start_types = self._start_types
              else:
                  start_types = set()
                  for type_list in self._function_types.values():
                      start_types.update(type_list)
              for start_type in start_types:
                  actions[START_SYMBOL].add(f"{START_SYMBOL} -> {start_type}")
              for name, function_type_list in self._function_types.items():
                  for function_type in function_type_list:
                      actions[str(function_type)].add(f"{function_type} -> {name}")
                      if isinstance(function_type, FunctionType):
                          return_type = function_type.return_type
                          arg_types = function_type.argument_types
                          right_side = f"[{function_type}, {', '.join(str(arg_type) for arg_type in arg_types)}]"
                          actions[str(return_type)].add(f"{return_type} -> {right_side}")
              self._nonterminal_productions = {key: sorted(value) for key, value in actions.items()}
          return self._nonterminal_productions
docs: Induces a grammar from the defined collection of predicates in this language and returns
          all productions in that grammar, keyed by the non-terminal they are expanding.
  
          This includes terminal productions implied by each predicate as well as productions for the
          `return type` of each defined predicate.  For example, defining a "multiply" predicate adds
          a "<int,int:int> -> multiply" terminal production to the grammar, and `also` a "int ->
          [<int,int:int>, int, int]" non-terminal production, because I can use the "multiply"
          predicate to produce an int.