打字稿:功能正常的Monad构造中的复杂自引用泛型类型,它在哪里工作以及在哪里失败

时间:2018-06-22 02:11:16

标签: typescript functional-programming monads

我想在类中有一个方法来展平嵌套实例。我将网上的工作片段拼凑在一起,可以使用外部功能进行展平;但是从类内部引用该函数会使Typescript变得非常慢,有bug和不稳定。结果是它计算出错误的类型。

这是相关代码

const Maybe = {
    of: <A>(a: A): Maybe<A> => {
    return isNully(a) ? new Just(a) : nothing
  }
}

// classes
class Just<A> { 
    readonly _A!: A
    constructor(readonly value: A) {}
    fold<B>(whenNothing: B, whenJust: (a: A) => B): B {
        return whenJust(this.value)
    }
    map<B>(fn: (a: A) => B): Maybe<B> {
        return Maybe.of(fn(this.value))
    }
    makeTrouble<B>(fn: (a: A) => B): Maybe<B> {
        return flatten(Maybe.of(fn(this.value)))
    }
}
class Nothing<A> { 
    readonly _A!: A
    static value: Maybe<never> = new Nothing()
    private constructor( ) { }
    fold<B>(whenNothing: B, whenJust: (a: A) => B): B {
        return whenNothing
    }
    map<B>(fn: (a: A) => B): Maybe<B> {
        return nothing
    }
    makeTrouble<B>(fn: (a: A) => B): Maybe<B> {
        return nothing
    }
}

// types
export type Maybe<A> = Nothing<A> | Just<A>

export type IsNestedMaybe<T> = T extends Maybe<Maybe<any>> ? 'T' : 'F'

export type Flatten<T extends Maybe<any>> = {
    T: Flatten<T['_A']>
    F: T
}[IsNestedMaybe<T>]


// values
const nothing = Nothing.value

// functions
const isNully = a => a === undefined || a === null || ( typeof a === 'number' && isNaN( a ) )
const isMaybe = <A>( obj : Maybe<A> | any ): obj is Maybe<A>  => {
  return ( obj instanceof Just )
}

function flatten<T extends Maybe<any>>(x: T): Flatten<T> {
    return x.fold(x, a => {
      if (isMaybe(a)) {
        return flatten(a)
      }
      return x
    })
}

export const head = <A>( arr : A[] ) => arr[ 0 ]
export const safeHead = <A>(as: Array<A>): Maybe<A> => {
  return isNully(as) ? nothing : new Just(head(as))
}

// Example 1 -- everything works

const foo1 = Maybe.of([1, 2, 3])
    .map(safeHead)
// foo1 :: Maybe<Maybe<number>>
const foo2 = flatten( foo1 )
// foo2 :: Maybe<number>

// Example 2 -- wat?

const foo3 = Maybe.of([1, 2, 3])
    .makeTrouble(safeHead)
// foo3 :: Maybe<Maybe<number>>

注意:如果您在自己的编辑器中尝试此操作并遇到速度变慢的情况,建议在代码底部同时注释掉makeTrouble方法和对foo3的{​​{1}}调用。

好,这是问题

为什么在使用Maybe.of(...).makeTrouble(...)函数时,此函数可以正确地计算类型?为什么flatten方法不能简单地调用makeTrouble函数呢?如何实现有效的flatten函数?

谢谢!

打字稿的引用报告问题:https://github.com/Microsoft/TypeScript/issues/25143

0 个答案:

没有答案