0

I'm trying to write a generic map function that can work with any Functor that looks like this:

interface Functor<A> {
    map <B>(fn: (a: A) => B): Functor<B>;
    inspect (): string;
}

function map2<X, Y, F1 extends Functor<X>, F2 extends Functor<Y>> (fn: (a: X) => Y) {
    return function $map (functor: F1): F2 {
        return functor.map(fn);
    }
}

However, when I write this to try and enforce the idea that map will take a functor of one type and return a functor of a (perhaps) different type, I get the following error:

Type 'Functor<Y>' is not assignable to type 'F2'.
    'Functor<Y>' is assignable to the constraint of type 'F2', but 'F2' could be instantiated with a different subtype of constraint 'Functor<Y>'

From what I understand after reading this answer, this error is telling me that after calling functor.map(fn) I could end up with a functor that isn't the same as the one that went in?

If this is the case, how can I write this to enforce that (e.g. I put a Maybe<A> in, I get a Maybe<B> out instead of an Either<B> or something). Do I need to move some of these constraints to the Functor interface?

If that isn't what is happening, what is, and how can I address it?

Logan Waite
  • 423
  • 5
  • 12

1 Answers1

0

quick and dirty via casting

function map2<X, Y,
    F1 extends Functor<X>,
    F2 extends Functor<Y>
> (fn: (a: X) => Y) {
    return function $map (functor: F1): F2 {
        return <F2> functor.map(fn);
    }
}

other casting falavor

function map3<X, Y,
    F1 extends Functor<X>,
    F2 extends Functor<Y>
    > (fn: (a: X) => Y) {
    return function $map (functor: F1): F2 {
        return  functor.map(fn) as F2;
    }
}

or simplify template parameter

type InferFunctorM<F> =  F extends Functor<infer As> ? As : never;


function map4<
    F1 extends Functor<any>,
    F2 extends Functor<any>
> (fn: (a: InferFunctorM<F1> ) => InferFunctorM<F2>) {
    return function $map (functor: F1): F2 {
        return <InferFunctorM<F2>> functor.map(fn);
    }
}

function map5<X, Y
    > (fn: (a: X) => Y) {
    return function $map (functor: Functor<X>): Functor<Y> {
        return  functor.map(fn);
    }
}
Andikac
  • 384
  • 2
  • 13