Hone logo
Hone
Problems

Type-Level Register Machine in TypeScript

This challenge asks you to implement a simplified register machine at the type level in TypeScript. Register machines are abstract computational models, and implementing one at the type level allows for powerful compile-time computation and code generation. This exercise will test your understanding of advanced TypeScript type manipulation, conditional types, and potentially recursive types.

Problem Description

You are to create a type-level register machine that can perform basic arithmetic operations on registers. The machine consists of a set of registers, each initialized with a numeric value. The machine is given a sequence of instructions, each of which specifies an operation to perform on one or more registers. The operations are:

  • set <register> <value>: Sets the value of a register to a given value.
  • add <register1> <register2>: Adds the value of register2 to register1.
  • sub <register1> <register2>: Subtracts the value of register2 from register1.

The machine should maintain the state of the registers throughout the execution of the instructions. The final state of the registers should be represented as a single type.

Key Requirements:

  • Register Definition: Define a type that represents the registers. This type should be a union of tuples, where each tuple represents a register and its value. For example: [10, 20, 30] represents three registers with initial values 10, 20, and 30.
  • Instruction Type: Define a type that represents an instruction. The instruction type should be a union of the instruction types for set, add, and sub.
  • Execution Function: Create a function execute that takes the initial register state and a list of instructions as input and returns the final register state. This function should be implemented using TypeScript's type system.
  • Type Safety: The execute function should be type-safe. It should ensure that the instructions are valid for the given register state and that the operations are performed correctly.

Expected Behavior:

The execute function should correctly update the register values based on the given instructions. The final register state should reflect the cumulative effect of all instructions.

Edge Cases to Consider:

  • Invalid register names (e.g., a register name that does not exist).
  • Instructions with invalid operands (e.g., adding a register to itself when it's not allowed).
  • Empty instruction list.
  • Initial register state with no registers.

Examples

Example 1:

type Registers = [10, 20, 30];
type Instructions = [
    { type: 'set', register: 0, value: 5 },
    { type: 'add', register1: 0, register2: 1 },
    { type: 'sub', register1: 2, register2: 0 }
];

// Expected Result: [15, 25, 25]
type Result = execute(Registers, Instructions);
// Result should be equivalent to [15, 25, 25]

Explanation: The first instruction sets register 0 to 5. The second instruction adds register 1 (20) to register 0 (5), resulting in 25 in register 0. The third instruction subtracts register 0 (25) from register 2 (30), resulting in 5 in register 2.

Example 2:

type Registers = [5, 10];
type Instructions = [
    { type: 'add', register1: 0, register2: 1 },
    { type: 'set', register: 1, value: 2 }
];

// Expected Result: [15, 2]
type Result = execute(Registers, Instructions);
// Result should be equivalent to [15, 2]

Explanation: The first instruction adds register 1 (10) to register 0 (5), resulting in 15 in register 0. The second instruction sets register 1 to 2.

Example 3: (Edge Case - Invalid Register)

type Registers = [10, 20];
type Instructions = [
    { type: 'set', register: 5, value: 5 } // Register 5 doesn't exist
];

// Expected Result: Type Error (Ideally, the compiler should catch this)
type Result = execute(Registers, Instructions);

Explanation: This instruction attempts to set a register that doesn't exist. The type system should ideally prevent this, or at least produce a clear error message.

Constraints

  • Register values must be non-negative integers.
  • Register names are represented by their index in the register tuple (starting from 0).
  • The number of registers is fixed at compile time.
  • The instruction list is also fixed at compile time.
  • The maximum number of instructions is 10. (This is primarily for complexity management; the solution should be scalable to more instructions if possible).
  • The register tuple can have a maximum length of 5.

Notes

  • This is a challenging problem that requires a deep understanding of TypeScript's type system.
  • Consider using conditional types and recursive types to implement the execute function.
  • Think about how to represent the register state and instructions as types.
  • Error handling is important. While full runtime error handling isn't required, the type system should ideally catch many common errors at compile time.
  • Start with a simple implementation and gradually add more features.
  • Focus on type safety and correctness. The goal is to create a type-level register machine that is both powerful and reliable.
Loading editor...
typescript