Scott Encoding in TypeScript
Scott encoding is a technique used in functional programming to represent data types with sum types (also known as tagged unions or discriminated unions) in a type-safe manner. It allows you to represent values that can be one of several distinct types, each identified by a unique tag. This challenge asks you to implement the core types and functions necessary to work with Scott encodings in TypeScript, enabling you to represent and manipulate data with greater type safety and clarity.
Problem Description
The goal is to create a system for representing and working with Scott encodings in TypeScript. This involves defining a base Scott type and then creating specific Scott encoding types for primitive types like number, string, and boolean. You'll also need to implement a function to "unwrap" a Scott encoding, extracting the underlying value based on its tag. The system should be type-safe, ensuring that you can only access the underlying value when the correct tag is present.
Key Requirements:
- Base
ScottType: Define a baseScotttype that includes atagproperty (string) and avalueproperty (any). - Specific Scott Encoding Types: Create Scott encoding types for
NumberScott,StringScott, andBooleanScott. These types should inherit fromScottand have their respectivevaluetypes (number, string, boolean). unwrapFunction: Implement a functionunwrap<T>(scott: Scott): T | undefinedthat takes aScottvalue as input and returns the underlying value if the tag matches the expected tag. If the tag doesn't match, it should returnundefined.- Type Safety: The
unwrapfunction should be type-safe, meaning that the TypeScript compiler should enforce that you're only unwrapping a value to the correct type.
Expected Behavior:
The unwrap function should correctly identify the tag of a Scott value and return the corresponding value. If the tag is incorrect, it should return undefined. The type system should prevent you from attempting to unwrap a NumberScott as a StringScott, for example.
Edge Cases to Consider:
- What happens if the input is not a
Scotttype at all? (While not strictly required, consider how you might handle this gracefully). - How do you ensure that the tags are unique and consistent across different Scott encoding types? (This is implicitly handled by the specific type definitions).
- What happens if the
valueproperty isnullorundefined? (The code should handle this without errors).
Examples
Example 1:
Input: const numScott: NumberScott = { tag: 'number', value: 123 };
Output: unwrap(numScott) // 123
Explanation: The tag 'number' matches the expected tag for a NumberScott, so the value 123 is returned.
Example 2:
Input: const strScott: StringScott = { tag: 'string', value: 'hello' };
Output: unwrap(strScott) // 'hello'
Explanation: The tag 'string' matches the expected tag for a StringScott, so the value 'hello' is returned.
Example 3:
Input: const boolScott: BooleanScott = { tag: 'boolean', value: true };
Output: unwrap(boolScott) // true
Explanation: The tag 'boolean' matches the expected tag for a BooleanScott, so the value true is returned.
Example 4:
Input: const mixedScott: Scott = { tag: 'number', value: 'wrong type' };
Output: unwrap(mixedScott) // undefined
Explanation: Although the tag is 'number', the value is not a number. The unwrap function should still return undefined.
Constraints
- The
tagproperty of theScotttype must be a string. - The
unwrapfunction must be generic, allowing it to unwrap to any type. - The code must be written in valid TypeScript.
- The solution should be reasonably efficient (avoid unnecessary iterations or complex logic).
Notes
Consider using discriminated unions to enforce type safety. The tag property acts as the discriminator. Think about how to leverage TypeScript's type system to ensure that the unwrap function only returns the correct type based on the tag. You don't need to handle invalid Scott types explicitly; the type system should provide sufficient protection.