import { TupleN } from '../util/types.js'; /** * This module contains basic methods for interacting with OCaml */ export { MlArray, MlPair, MlList, MlOption, MlBool, MlBytes, MlResult, MlUnit, MlString, MlTuple, MlArrayOptionalElements, }; // ocaml types type MlPair = [0, X, Y]; type MlArray = [0, ...T[]]; type MlList = [0, T, 0 | MlList]; type MlOption = 0 | [0, T]; type MlBool = 0 | 1; type MlResult = [0, T] | [1, E]; type MlUnit = 0; // custom types type MlArrayOptionalElements = { [K in keyof MlArray]: MlArray[K] extends 0 ? 0 : MlOption; }; /** * js_of_ocaml representation of a byte array, * see https://github.com/ocsigen/js_of_ocaml/blob/master/runtime/mlBytes.js */ type MlBytes = { t: number; c: string; l: number }; type MlString = MlBytes; const MlArray = { to(arr: T[]): MlArray { return [0, ...arr]; }, from([, ...arr]: MlArray): T[] { return arr; }, map([, ...arr]: MlArray, map: (t: T) => S): MlArray { return [0, ...arr.map(map)]; }, mapTo(arr: T[], map: (t: T) => S): MlArray { return [0, ...arr.map(map)]; }, mapFrom([, ...arr]: MlArray, map: (t: T) => S): S[] { return arr.map(map); }, }; const MlPair = Object.assign( function MlTuple(x: X, y: Y): MlPair { return [0, x, y]; }, { from([, x, y]: MlPair): [X, Y] { return [x, y]; }, first(t: MlPair): X { return t[1]; }, second(t: MlPair): Y { return t[2]; }, } ); const MlBool = Object.assign( function MlBool(b: boolean): MlBool { return b ? 1 : 0; }, { from(b: MlBool) { return !!b; }, } ); const MlOption = Object.assign( function MlOption(x?: T): MlOption { return x === undefined ? 0 : [0, x]; }, { from(option: MlOption): T | undefined { return option === 0 ? undefined : option[1]; }, map(option: MlOption, map: (t: T) => S): MlOption { if (option === 0) return 0; return [0, map(option[1])]; }, mapFrom(option: MlOption, map: (t: T) => S): S | undefined { if (option === 0) return undefined; return map(option[1]); }, mapTo(option: T | undefined, map: (t: T) => S): MlOption { if (option === undefined) return 0; return [0, map(option)]; }, isNone(option: MlOption): option is 0 { return option === 0; }, isSome(option: MlOption): option is [0, T] { return option !== 0; }, } ); const MlResult = { ok(t: T): MlResult { return [0, t]; }, unitError(): MlResult { return [1, 0]; }, }; /** * tuple type that has the length as generic parameter */ type MlTuple = N extends N ? number extends N ? [0, ...T[]] // N is not typed as a constant => fall back to array : [0, ...TupleRec] : never; type TupleRec = R['length'] extends N ? R : TupleRec; type Tuple = [T, ...T[]] | []; const MlTuple = { map, B>( [, ...mlTuple]: [0, ...T], f: (a: T[number]) => B ): [0, ...{ [i in keyof T]: B }] { return [0, ...mlTuple.map(f)] as any; }, mapFrom([, ...mlTuple]: MlTuple, f: (a: T) => B): B[] { return mlTuple.map(f); }, mapTo | TupleN, B>( tuple: T, f: (a: T[number]) => B ): [0, ...{ [i in keyof T]: B }] { return [0, ...tuple.map(f)] as any; }, };