Search code examples
typescriptgenericscons

How to generate a generic TypeScript type signature for a cons cell?


I'm struggling to find a generic type signature for a cons cell, that allows for the selector function to be typed.

I.e. The code I want to be typed:

const cons = (head, tail) => selector => selector(head, tail);

I want to assign a generic type, where type T is the head and type V is the tail.

Some of my elaborate typing attempts have been:

const cons: <T,V>(head: T, rest: V) => (selector: (head: T, rest: V) => T | V) => T | V 
    = (head, rest) => selector => selector(head, rest);

The problem with the above code, is that I cannot find a type signature that works for the selector function head, with the implementation:

const head = list => list((head, rest) => head)

I always get the error "Expected 2 arguments but got 1." under list((head, rest) => head).

I just can't get the generic type to work! Any help would be greatly appreciated! Thank you!

Edit - Added example of expected behaviour (without types)

const cons = (head, tail) => selector => selector(head, tail);
const head = list => list((head, tail) => head);
const rest = list => list((head, tail) => tail);
let someList = cons(1, cons(2, cons(3, null)));
head(someList);
// returns: 1
rest(someList);
// returns: cons(2, cons(3, null))

Solution

  • Your const type is exactly the same as mine. I just split the many parts for clarity. The only new type I have added is the selectorFuncWrapper, which I think is what you were looking for.

    The original code is preserved. Check if it is good for you.

    type selector<H, T> = (head: H, tail: T) => (H | T);
    type selectorFunc<H, T> = (selector: selector<H, T>) => (H | T);
    type consFunc<H, T> = (head: H, tail: T) => selectorFunc<H, T>;
    type selectorFuncWrapper<H, T> = (list: selectorFunc<H, T>) => selectorFunc<H, T>;
    
    const cons: consFunc<number, any> = (head, tail) => selector => selector(head, tail);
    
    const head: selectorFuncWrapper<number, any> = list => list((head, tail) => head);
    const rest: selectorFuncWrapper<number, any> = list => list((head, tail) => tail);
    
    let someList = cons(1, cons(2, cons(3, null)));
    
    head(someList);
    // returns: 1
    rest(someList);
    // returns: cons(2, cons(3, null))