Skip to content

Instantly share code, notes, and snippets.

@hediet
Last active March 11, 2024 15:05
Show Gist options
  • Save hediet/63f4844acf5ac330804801084f87a6d4 to your computer and use it in GitHub Desktop.
Save hediet/63f4844acf5ac330804801084f87a6d4 to your computer and use it in GitHub Desktop.
Proof that TypeScript's Type System is Turing Complete
type StringBool = "true"|"false";


interface AnyNumber { prev?: any, isZero: StringBool };
interface PositiveNumber { prev: any, isZero: "false" };

type IsZero<TNumber extends AnyNumber> = TNumber["isZero"];
type Next<TNumber extends AnyNumber> = { prev: TNumber, isZero: "false" };
type Prev<TNumber extends PositiveNumber> = TNumber["prev"];


type Add<T1 extends AnyNumber, T2> = { "true": T2, "false": Next<Add<Prev<T1>, T2>> }[IsZero<T1>];

// Computes T1 * T2
type Mult<T1 extends AnyNumber, T2 extends AnyNumber> = MultAcc<T1, T2, _0>;
type MultAcc<T1 extends AnyNumber, T2, TAcc extends AnyNumber> = 
		{ "true": TAcc, "false": MultAcc<Prev<T1>, T2, Add<TAcc, T2>> }[IsZero<T1>];

type _0 = { isZero: "true" };
type _1 = Next<_0>;
type _2 = Next<_1>;
type _3 = Next<_2>;
type _4 = Next<_3>;
type _5 = Next<_4>;
type _6 = Next<_5>;
type _7 = Next<_6>;
type _8 = Next<_7>;
type _9 = Next<_8>;

type Digits = { 0: _0, 1: _1, 2: _2, 3: _3, 4: _4, 5: _5, 6: _6, 7: _7, 8: _8, 9: _9 };
type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type NumberToType<TNumber extends Digit> = Digits[TNumber]; // I don't know why typescript complains here.

type _10 = Next<_9>;
type _100 = Mult<_10, _10>;

type Dec2<T2 extends Digit, T1 extends Digit>
	= Add<Mult<_10, NumberToType<T2>>, NumberToType<T1>>;



type Stack = EmptyStack | NonEmptyStack;
interface NonEmptyStack { prev: any, isEmpty: "false", item: any };
interface EmptyStack { isEmpty: "true" };

type IsEmpty<TStack extends Stack> = TStack["isEmpty"];
type Push<TItem, TStack extends Stack> = { prev: TStack, item: TItem, isEmpty: "false" };
type Peek<TStack extends NonEmptyStack> = TStack["item"];
type Pop<TStack extends NonEmptyStack> = TStack["prev"]; 
type PeekOrDefault<TStack extends Stack, TDefault> = {
	"true": TDefault,
	"false": Peek<TStack>
}[IsEmpty<TStack>];
type PopIfNotEmpty<TStack extends Stack> = {
	"true": TStack,
	"false": Pop<TStack>
}[IsEmpty<TStack>];


type TapeLike = { left: Stack, current: any, right: Stack, default: string }
type Left<TTape extends TapeLike> = TTape["left"];
type Right<TTape extends TapeLike> = TTape["right"];

type ConfigLike<TAutomata> = { state: keyof TAutomata, tape: TapeLike, halt: StringBool };
type AutomataLike = {
	[state: string]: {
		[symbol: string]: InstructionLike;
	}
};

type InstructionLike = { write: string, move: "r" | "_" | "l", next: string, halt: StringBool };

type NextConfig<TAutomata extends AutomataLike, TConfig extends ConfigLike<TAutomata>> 
	= NextConfig1<TConfig, TAutomata[TConfig["state"]][TConfig["tape"]["current"]]>;
type NextConfig1<TConfig extends ConfigLike<any>, TNextInstruction extends InstructionLike> 
	= NextConfig2<TConfig["tape"], TNextInstruction["write"], TNextInstruction>;
type NextConfig2<TTape, TWrite, TNextInstruction extends InstructionLike> = {
	state: TNextInstruction["next"],
	halt: TNextInstruction["halt"],
	tape: {
		"r": {
			left: Push<TWrite, Left<TTape>>,
			current: PeekOrDefault<Right<TTape>, TTape["default"]>,
			right: PopIfNotEmpty<Right<TTape>>,
			default: TTape["default"]
		},
		"_": {
			left: Left<TTape>,
			current: TWrite,
			right: Right<TTape>,
			default: TTape["default"]
		},
		"l": {
			left: PopIfNotEmpty<Left<TTape>>,
			current: PeekOrDefault<Left<TTape>, TTape["default"]>,
			right: Push<TWrite, Right<TTape>>,
			default: TTape["default"]
		}
	}[TNextInstruction["move"]]
}

type Simplify<T> = { [TKey in keyof T]: T[TKey] }

type Run<TAutomata, TConfig extends { halt: StringBool }, TSteps extends AnyNumber> = {
	true: TConfig,
	false: { false: Run<TAutomata, Simplify<NextConfig<TAutomata, TConfig>>, Prev<TSteps>>, true: TConfig }[IsZero<TSteps>]
}[TConfig["halt"]];

type RunUnbound<TAutomata, TConfig extends { halt: StringBool }> = {
	true: TConfig,
	false: RunUnbound<TAutomata, Simplify<NextConfig<TAutomata, TConfig>>>
}[TConfig["halt"]];

type InitialConfig = { state: "0", halt: "false", tape: { left: EmptyStack, current: "0", right: EmptyStack, default: "0" } };

type BusyBeaver4State = {
	"0": {
		"0": { write: "1", move: "r", next: "1", halt: "false" },
		"1": { write: "1", move: "l", next: "1", halt: "false" },
	},
	"1": {
		"0": { write: "1", move: "l", next: "0", halt: "false" },
		"1": { write: "0", move: "l", next: "2", halt: "false" },
	},
	"2": {
		"0": { write: "1", move: "r", next: "2", halt: "true" },
		"1": { write: "1", move: "l", next: "3", halt: "false" },
	},
	"3": {
		"0": { write: "1", move: "r", next: "3", halt: "false" },
		"1": { write: "0", move: "r", next: "0", halt: "false" },
	},
}

type S9 = Run<BusyBeaver4State, InitialConfig, _9>;
@AverageHelper
Copy link

AverageHelper commented Jan 19, 2022

I am amazed this works!

@orendecor
Copy link

Nice job @ricokahler.
Small mistake (not a big deal) I think the moveLeft and moveRight are opposite, this is should be the right form:

type MoveLeft<T extends Tape> = {
  left: RemoveLast<T['left']>
  current: Last<T['left']>
  right: `${T['current']}${T['right']}`
}

type MoveRight<T extends Tape> = {
  left: `${T['left']}${T['current']}`
  current: First<T['right']>
  right: RemoveFirst<T['right']>
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment