代码之家  ›  专栏  ›  技术社区  ›  Chet

如何做自由单子喜欢的东西与类型脚本?

  •  2
  • Chet  · 技术社区  · 7 年前

    我记得我浏览过某人的Haskell代码库,当我看到每个API处理程序的类型定义完全解释了函数调用的所有副作用时,我非常感兴趣。所以我开始尝试用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很生气我有一个递归的语言类型定义。真倒霉。有办法绕过这件事吗?

    接下来我想做的是通过扩展Promises来使用ES6 async/await语法。这很混乱,但我想出了一个延长承诺的方法。

    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 回复  |  直到 7 年前
    推荐文章