Implement toy-language types and generic substitution
Company: OpenAI
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates understanding of type systems, AST-style type representations, and generic type variable resolution by requiring implementation of Node and Function models and substitution of generics to infer concrete return types.
Part 1: Format Toy-Language Types and Function Signatures
Constraints
- kind is either 'node' or 'function'
- Each leaf is a non-empty string
- Tuple types are represented only by Python lists
- 0 <= number of function parameters <= 10^3
- Total number of nodes across the input structure is at most 10^4
- Maximum nesting depth is at most 200
Examples
Input: ('node', 'int')
Expected Output: 'int'
Explanation: A leaf node prints as its exact stored name.
Input: ('node', ['int', ['T1', 'str']])
Expected Output: '[int,[T1,str]]'
Explanation: Nested tuple types are formatted recursively with no spaces.
Input: ('function', (['int', 'T1'], ['T1', 'str']))
Expected Output: '(int,T1) -> [T1,str]'
Explanation: Each parameter and the return type are formatted using the same node rules.
Input: ('function', ([], 'bool'))
Expected Output: '() -> bool'
Explanation: Edge case: a function may have zero parameters.
Input: ('node', ['T9'])
Expected Output: '[T9]'
Explanation: Edge case: a single-element tuple still uses brackets.
Solution
def solution(kind, data):
class Node:
def __init__(self, node_type):
if isinstance(node_type, str):
self.name = node_type
self.children = None
elif isinstance(node_type, list):
self.name = None
self.children = [Node(child) for child in node_type]
else:
raise TypeError('node_type must be a string or a list')
def to_str(self):
if self.children is None:
return self.name
return '[' + ','.join(child.to_str() for child in self.children) + ']'
class Function:
def __init__(self, parameters, output_type):
self.parameters = [Node(param) for param in parameters]
self.output_type = Node(output_type)
def to_str(self):
param_text = ','.join(param.to_str() for param in self.parameters)
return '(' + param_text + ') -> ' + self.output_type.to_str()
if kind == 'node':
return Node(data).to_str()
if kind == 'function':
parameters, output_type = data
return Function(parameters, output_type).to_str()
raise ValueError('invalid kind')Time complexity: O(N), where N is the total number of nodes in the given type expression or function signature.. Space complexity: O(N), due to building the recursive Node structure..
Hints
- Use one recursive helper to format a type expression: strings return themselves, lists format each child and join with commas.
- For a function signature, reuse the same type-formatting helper for every parameter and for the return type.
Part 2: Resolve Generic Return Types in Toy-Language
Constraints
- 0 <= len(actual_parameters), len(expected_parameters) <= 10^3
- Tuple types are represented only by Python lists
- Total number of nodes across all input type expressions is at most 10^4
- Maximum nesting depth is at most 200
- Primitive type names are lowercase strings
- Generic names start with an uppercase letter
Examples
Input: (['int', 'str', 'int', 'int'], ['T1', 'T2', 'int', 'T1'], ['T1', 'T2'])
Expected Output: '[int,str]'
Explanation: T1 resolves to int and T2 resolves to str.
Input: ([['str', 'float'], ['int', 'str'], 'int'], [['T1', 'float'], 'T2', 'T3'], ['T3', 'T1'])
Expected Output: '[int,str]'
Explanation: Nested tuples are matched recursively before substituting the output.
Input: (['int', 'str'], ['T1', 'T1'], 'T1')
Expected Output: 'ERROR'
Explanation: T1 cannot be bound to both int and str in the same call.
Input: ([], [], 'bool')
Expected Output: 'bool'
Explanation: Edge case: a zero-parameter function with a concrete return type is valid.
Input: (['int'], ['int'], 'T1')
Expected Output: 'ERROR'
Explanation: The output generic T1 was never bound by the inputs.
Solution
def solution(actual_parameters, expected_parameters, output_type):
def is_generic(expr):
return isinstance(expr, str) and expr != '' and expr[0].isupper()
def clone(expr):
if isinstance(expr, list):
return [clone(part) for part in expr]
return expr
def same(a, b):
if isinstance(a, str) and isinstance(b, str):
return a == b
if isinstance(a, list) and isinstance(b, list) and len(a) == len(b):
return all(same(x, y) for x, y in zip(a, b))
return False
def format_type(expr):
if isinstance(expr, str):
return expr
return '[' + ','.join(format_type(part) for part in expr) + ']'
substitutions = {}
def unify(expected, actual):
if isinstance(expected, str):
if is_generic(expected):
if expected in substitutions:
return same(substitutions[expected], actual)
substitutions[expected] = clone(actual)
return True
return isinstance(actual, str) and expected == actual
if isinstance(expected, list):
if not isinstance(actual, list) or len(expected) != len(actual):
return False
for expected_child, actual_child in zip(expected, actual):
if not unify(expected_child, actual_child):
return False
return True
return False
def substitute(expr):
if isinstance(expr, str):
if is_generic(expr):
if expr not in substitutions:
raise ValueError('unbound generic')
return clone(substitutions[expr])
return expr
if isinstance(expr, list):
return [substitute(part) for part in expr]
raise ValueError('invalid type expression')
if len(actual_parameters) != len(expected_parameters):
return 'ERROR'
for expected, actual in zip(expected_parameters, actual_parameters):
if not unify(expected, actual):
return 'ERROR'
try:
concrete_output = substitute(output_type)
except ValueError:
return 'ERROR'
return format_type(concrete_output)Time complexity: O(N), where N is the total number of nodes across all parameter types and the output type.. Space complexity: O(N), for the substitution table, recursion stack, and constructed substituted output..
Hints
- Walk expected and actual types together recursively, building a dictionary from generic name to concrete subtree.
- After matching the inputs, run a second recursive pass over the output type to substitute every generic.