如何用TypesScript做类似Monad的免费东西?

时间:2018-09-27 03:42:24

标签: typescript

我记得当我看到每个API处理程序的类型定义完全解释了该函数所调用的所有副作用时,真的迷住了某个人的Haskell代码库。因此,我着手尝试使用TypeScript完成类似的操作。这就是我到目前为止所拥有的。

export type More = {
    then: (...args: any[]) => Thenable
}

type Done = {
    then: undefined
}

const done = { then: undefined } as Done

type Thenable = More | Done

type RedisRead<Key extends string, Then extends Thenable> = {
    type: "RedisRead"
    key: Key
    then: (value: string) => Then
}

type RedisWrite<Key extends string, Then extends Thenable> = {
    type: "RedisWrite"
    key: Key
    value: string
    then: () => Then
}

function readRedis<Key extends string, Then extends Thenable>(
    key: Key,
    then: (value: string) => Then
): RedisRead<Key, Then> {
    return {
        type: "RedisRead",
        key: key,
        then: then,
    }
}

function writeRedis<Key extends string, Then extends Thenable>(
    key: Key,
    value: string,
    then: () => Then
): RedisWrite<Key, Then> {
    return {
        type: "RedisWrite",
        key: key,
        value: value,
        then: then,
    }
}

// Inferred type: RedisRead<"x", RedisWrite<"y", Done>>
function doSomething() {
    return readRedis("x", value => {
        return writeRedis("y", value, () => {
            return done
        })
    })
}

以上代码的结果是doSomething的推断类型为RedisRead<"x", RedisWrite<"y", Done>>。很整洁吧!

我们还没有完全结束。我想写一个可以解释这种语言的解释器。它应该看起来像这样:

type Language = Done | RedisRead<any, Language> | RedisWrite<any, Language>

function interpret(item: Language) {
    if (item.type === "Done") {
        console.log("Done")
    } else if (item.type === "RedisWrite") {
        redis.add(key, value, () => interpret(item.then()))
    } else if (item.type === "RedisRead") {
        redis.get(key, (value) => interpret(item.then(value)))
    }
}

但是,TypeScript很生气,因为我有Language的递归类型定义。真是可惜。有什么解决方法吗?

我想做的下一件事是通过扩展Promises使用ES6异步/等待语法。它很杂乱,但是我想出了一种扩展Promises的方法。

class RedisRead<Key extends string, T extends Promise<any>> extends Promise<
    string
> {
    resolve: (value: any) => void
    reject: (value: any) => void
    constructor(public key: Key) {
        super((resolve, reject) => {
            this.resolve = resolve
            this.reject = reject
        })
    }
    type: "RedisRead"
    then: (fn?: null | ((value: string) => T)) => T
}

class RedisWrite<Key extends string, T extends Promise<any>> extends Promise<
    string
> {
    resolve: (value: any) => void
    reject: (value: any) => void
    constructor(public key: Key, public value: string) {
        super((resolve, reject) => {
            this.resolve = resolve
            this.reject = reject
        })
    }
    type: "RedisWrite"
    then: (fn?: null | ((value: any) => T)) => T
}




async function doSomething() {
    const value = await new RedisRead("x")
    return new RedisWrite("y", value)
}

它似乎可以工作,但推断的类型为Promise<any>

无论如何,我认为这种架构将非常强大。如果您能找到解决这些问题的方法,请告诉我。

0 个答案:

没有答案