Проблема реализации функтора в машинописи

Я читал серию блогов Тома Хардинга по спецификации фэнтези-лэнд, и сегодня днем ​​я играл с реализацией функтора в машинописи.

class Just<T> {
  private x: T

  constructor (x: T) {
    this.x = x
  }

  map <R>(f: (a: T) => R): Just<R> {
    return new Just(f(this.x))
  }
}

class Nothing<T> {
  private x: T

  constructor (_: T) {
    // noop
  }

  map <R>(_: (a: T) => R): Nothing<R> {
    return new Nothing<R>(undefined)
  }
}

type Maybe<T> = Just<T> | Nothing<T>

const map1 = <T, R>(f: (a: T) => R, a: Maybe<T>): Maybe<R> => {
  return a.map(f)
}

К несчастью, a.map(f) выше приводит к следующей ошибке времени компиляции:

[ts] Cannot invoke an expression whose type lacks a call signature. 
Type '(<R>(f: (a: T) => R) => Just<R>) | (<R>(_: (a: T) => R) => Nothing<R>)' 
has no compatible call signatures.

Я чувствую, что это должно работать, хотя... Я сделал более простой пример, который не является функтором, но который использует дженерики в большинстве случаев:

class A<T> {
  private x: T

  constructor (x: T) {
    this.x = x
  }

  func <R>(f: (a: T) => R): R { // this is returning R not A<R>
    return f(this.x)
  }
}

class B<T> {

  constructor (_: T) {
    //
  }

  func <R>(_: (a: T) => R): R {
    return undefined
  }
}

type C<T> = A<T> | B<T>

const func = <T, R>(f: (a: T) => R, a: C<T>): R => {
  return a.func(f)
}

Этот код компилируется просто отлично. Если я изменю типы возвращаемых функций на A<R> а также B<R> (т.е. если карта вернется C<R>) тогда я получаю ту же ошибку, что и выше.

Вот мне и интересно: что случилось? Это какая-то сумасшедшая вещь контравариантности / ковариантности? Это ожидаемое поведение машинописи? Это я что-то упустил? (или это ничто ^o^//).


РЕДАКТИРОВАТЬ: я попытался повторно реализовать вышеизложенное с наследованием вместо объединения:

abstract class Maybe<T> {
  protected x: T

  constructor (x: T) {
    this.x = x
  }

  abstract map <R>(f: (a: T) => R): Maybe<R>
}

class Just<T> extends Maybe<T> {
  constructor (x: T) {
    super(x)
  }

  map <R>(f: (a: T) => R): Just<R> {
    return new Just(f(this.x))
  }
}

class Nothing<T> extends Maybe<T> {
  constructor (_: T) {
    super(undefined)
  }

  map <R>(f: (a: T) => R): Nothing<R> {
    return new Nothing(undefined)
  }
}

const map2 = <T, R>(f: (a: T) => R, a: Maybe<T>): Maybe<R> => {
  return a.map(f)
}

Работает просто отлично! Что дает, машинопись!?

Даже думал, что вышесказанное работает, это не то, что я хочу. Just не "расширяется" Maybe, Maybe это союз Just а также Nothing, Это мой путь к шоссе


РЕДАКТИРОВАТЬ: РЕДАКТИРОВАТЬ: я попробовал это снова, используя интерфейс для Functor

// tslint:disable-next-line:interface-name
interface Functor<T> {
  map: <R>(f: (a: T) => R) => Functor<R>
}

class Just<T> implements Functor<T> {
  private readonly x: T

  constructor (x: T) {
    this.x = x
  }

  map <R>(f: (a: T) => R): Just<R> {
    return new Just(f(this.x))
  }
}

class Nothing<T> implements Functor<T> {
  constructor (_: T) {
    // nop
  }

  map <R>(_: (a: T) => R): Nothing<R> {
    return new Nothing(undefined)
  }
}

type Maybe<T> = Functor<T>

const map3 = <T, R>(f: (a: T) => R, a: Maybe<T>): Maybe<R> => {
  return a.map(f)
}

Это тоже работает, и я согласен с тем, что Just реализует интерфейс Functor, потому что, ну... так и есть. Я все еще не доволен type Maybe<T> = Functor<T> потому что он переопределяет тип (членов Functor больше, чем Maybe?)

Кроме того, я предполагаю, что карта должна не просто возвращать Functor, но и тот же Functor, как карта Maybe возвращает значение Maybe (а не просто любой Functor). Я начинаю понимать, почему нам нужны типы с более высоким родом, чтобы представлять подобные вещи?


РЕДАКТИРОВАТЬ: Добавление второго универсального для учета вида функтора, кажется, работает. Теперь карта Just должна вернуть Just.

// tslint:disable-next-line:interface-name
interface Functor<K, T> {
  map: <R>(f: (a: T) => R) => Functor<K, R>
}

И тогда я определяю, может быть, так:

type Maybe<T> = Functor<Just<T> | Nothing<T>, T>

Скрещенные пальцы.

0 ответов

Другие вопросы по тегам