无法在打字稿

时间:2018-04-28 05:37:41

标签: typescript polymorphism category-theory fantasyland

我一直在阅读Tom Harding的关于幻想土地规范的博客系列,今天下午我正在玩打字稿中的仿函数。

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>(即,如果map返回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没有&#34;延伸&#34; MaybeMaybeJustNothing的联盟。这是我的高速公路方式

编辑:编辑:我使用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)
}

这也有效,而且我可以说实现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>
}

然后我将Maybe定义为:

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

手指交叉。

0 个答案:

没有答案