typescript で非同期リソース作成のロールバック関数を合成する

## 動機

マイクロサービスの Saga パターンのように処理が途中で失敗したときにロールバックしてそれまで作成したリソースを削除していくような関数が欲しい。

具体的には、

このような型の失敗する可能性がある非同期リソース作成関数と

```ts
// 失敗する可能性がある
async function createResource(o: {}): Promise<{resourceId: ResourceId;}>;
```

このような型の失敗する可能性がある非同期リソース削除関数があったとき

```ts
// 失敗する可能性がある
async function deleteResource(o: {resourceId: ResourceId;}): Promise<{}>;
```

リソース作成を続けて行いたいので

```ts
const resourceAId = await createResourceA({});
const resourceBId = await createResourceB({});
return {resourceAId, resourceBId};
```

と書きたい。

けれど、もし createResourceB が失敗したときに、作成済みの resourceAId を削除したいので、 try-catch を使って

```ts
const resourceAId = await createResourceA({});
try{
  const resourceBId = await createResourceB({});
  return {resourceAId, resourceBId};
}catch(err){
  await deleteResourceA(resourceAId);
  throw err;
}
```

のように書かねばならず、煩雑である。

そこでこの２つの関数を合成して

```ts
const createResourceRollbackable = createRollbackable(
  createResource,
  deleteResource,
  ({}, { resourceId }) => ({ resourceId })
);
```

非同期継続コールバック関数の中でエラーが起きた時に、それまでに作成したリソースを削除する関数を生成したい

```ts
const {
  resourceAId,
  resourceBId
} =  await createResourceARollbackable({}, async ({ resourceAId }) => {
  return await createResourceBRollbackable({}, async ({ resourceBId }) => {
    return {resourceAId, resourceBId};
  });
});
```

## 合成関数の実装

そのような合成関数の実装は以下のようになる

```ts
/** callback と delete_ の両方のエラーを返しうる */
export function createRollbackable<T, U, V, W>(
  create: (o: T) => Promise<U>,
  delete_: (o: V) => Promise<W>,
  composeDeleteArg: (o: T, p: U) => V
): <RET>(o: T, callback: (o: U) => Promise<RET>) => Promise<RET> {
  const stack = new Error().stack;
  return async (o, callback) => {
    const resource = await create(o);
    const delopt = composeDeleteArg(o, resource);
    try {
      const ret = await callback(resource);
      return ret;
    } catch (err) {
      console.error(
        "createWithFunc error",
        stack,
        util.inspect([o, delopt], { depth: 30 }),
        err
      );
      await delete_(delopt);
      throw err;
    }
  };
}
```

## モナド

このパターンは明らかにモナドなので do 構文が欲しい。
JS で do 構文を実現するにはジェネレータを使えばよい。

具体的には

```ts
function*(){
  const resourceAId = yield createResourceARollbackable({});
  const resourceBId = yield createResourceBRollbackable({});
  return {resourceAId, resourceBId};
}
```

のように書けたらうれしいし書けるはず。

 `createResourceARollbackable` が継続を受け取らず、 Promise とは異なるモナド値を返せばよい。
しかし Promise とは異なるモナドなのでこのコンテキストでは await できず不便である。

```ts
function*(){
  const resourceAId = yield createResourceARollbackable({});
  const resourceBId = yield createResourceBRollbackable({});
  await sleep(1000); // <- ここは Rollbackable モナドの文脈なので await できない！
  return {resourceAId, resourceBId};
}
```

そこで `runRollbackAsync` や `liftAsync` のような関数が導入できるはずだ。

```ts
const {resourceAId, resourceBId} = await runRollbackAsync(function*(){
  const resourceAId = yield createResourceARollbackable({});
  const resourceBId = yield createResourceBRollbackable({});
  yield liftAsync(async ()=>{
    await sleep(1000);
  });
  return {resourceAId, resourceBId};
});
await sleep(1000);
return {resourceAId, resourceBId};
```

しかし TypeScript の型システムの Generator の扱いの面倒くささから実装はあきらめた。