0

The discussion in implement callable interface is useful but does not completely my answer my question.

interface lol {
    (a: number): (b: number) => string
    // (a: string): (b: string) => string  // overloaded will not work
}
let l: lol = function(a: number) {
  return (b: number) => {
    return (a + b).toString()
  }
}

This snippet works but when the overloaded function is uncommented it fails. I've tried various methods and haven't figure out how to implement the overloaded version.

A slightly convoluted but real world implementation I'm trying to understand is fp-ts style interfaces. Ignoring the complex types it's an overloaded callable interface is what I understand. How would such a thing have a concrete implementation.

export interface Traverse1<T extends URIS> {
  <F extends URIS4>(F: Applicative4<F>): <A, S, R, E, B>(
    ta: Kind<T, A>,
    f: (a: A) => Kind4<F, S, R, E, B>
  ) => Kind4<F, S, R, E, Kind<T, B>>
  <F extends URIS3>(F: Applicative3<F>): <A, R, E, B>(
    ta: Kind<T, A>,
    f: (a: A) => Kind3<F, R, E, B>
  ) => Kind3<F, R, E, Kind<T, B>>
  <F extends URIS3, E>(F: Applicative3C<F, E>): <A, R, B>(
    ta: Kind<T, A>,
    f: (a: A) => Kind3<F, R, E, B>
  ) => Kind3<F, R, E, Kind<T, B>>
  <F extends URIS2>(F: Applicative2<F>): <A, E, B>(
    ta: Kind<T, A>,
    f: (a: A) => Kind2<F, E, B>
  ) => Kind2<F, E, Kind<T, B>>
  <F extends URIS2, E>(F: Applicative2C<F, E>): <A, B>(
    ta: Kind<T, A>,
    f: (a: A) => Kind2<F, E, B>
  ) => Kind2<F, E, Kind<T, B>>
  <F extends URIS>(F: Applicative1<F>): <A, B>(ta: Kind<T, A>, f: (a: A) => Kind<F, B>) => Kind<F, Kind<T, B>>
  <F>(F: Applicative<F>): <A, B>(ta: Kind<T, A>, f: (a: A) => HKT<F, B>) => HKT<F, Kind<T, B>>
}

2 Answers 2

1

The implementation declaration must satisfy all of the overloads stated by the interface so, in your case, it might end up looking something like this:

let l: lol = function <T extends number | string>(a: T): (b: T) => string {
    throw Error("not implemented");
}

In my experience, you'll need to do a lot of type-checking and casting to get the implementation right.

Playground Link

Sign up to request clarification or add additional context in comments.

1 Comment

Aah now I get it. Atleast in the fp-ts example by using <F extends ...> some overloads can be eliminated and not need to be implemented. Adding possible solution as separate answer.
0

After @spender's helpful explanation I realized what the fp-ts style interface is actually doing. By having <F extends ...> check it is possible to resolve the overloaded functions to a single concrete implementation as in the example below.

interface lol {
    <T extends number>(a: T): (b: T) => string
    <T extends string>(a: T): (b: T) => string
}

let l: lol = function(a: number) {
    return (b: number) => {
        return (a + b).toString()
    }
}

This instantiates the T as number so the <T extends string> overloading is never resolved (?? is that the right word for it).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.