0

有没有办法将 IOEither 和 TaskEither 表示为一个也包括tryCatch?

我目前将通过 HTTP 使用 API,因此使用 TaskEither 是有意义的,但预计此代码将“离家更近”迁移,并且在那时将其设为有意义IOEither。所以我想写一个无标签风格的消费者界面

interface EngineRepository<M extends URIS2> {
  calculateNumber: (i:SomeData) => Kind2<M, DomainError, number>
}

const getRepo = <M>(m:M extends URIS2):EngineRepository<M> => ({
  calculateNumber: someCalculation(m)()
})

const calculateNumber = <M>(m:M extends URIS2) => flow(/* M.chain, M.map, etc. works great! */)

到目前为止,一切都很好!然而,虽然有一个tryCatchfor Option、Either、TaskEither、IOEither 等,但我可以说它不是任何接口的一部分。所以我正在尝试创建自己的:

interface Tryable<M extends URIS2> extends Monad2<M> {
  tryCatch: <E,A>(f:Lazy<A>, onError: (reason:unknown) => E) => Kind2<M, E, A>
}

const calculateNumber = <M>(m:M extends URIS2) => 
  flow(/* M.tryCatch works great now! */)

这里的问题IOError是同步,所以f:Lazy<A>很好,TaskEither是异步的,所以它需要f:Lazy<Promise<A>>改为。

有没有更好的方法来解决这个问题,或者这是不可能的?我是否需要始终使用 TaskEither,然后添加一个将 IOEither 转换为 TaskEither 并放弃无标记决赛的步骤?

4

1 回答 1

0

我有一个临时的解决方案,感觉很hacky:

interface Tryable<M extends URIS2, ThunkType extends 'Task'|'IO'> extends MonadIO2<M> {
  tryCatch: <E, A>(f:Kind<ThunkType, A>, onError: (e:unknown)=>E) => Kind2<M, E, A>
}

const te: Tryable<'TaskEither', 'Task'> = { ...TE.taskEither, tryCatch: TE.tryCatch}
const ioe: Tryable<'IOEither', 'IO'> = { ...IOE.ioEither, tryCatch: IOE.tryCatch}

pipe(te.tryCatch(()=>Promise.resolve(5),()=>'error'), te.map(num=>`${num}`)) // TaskEither<'error',string>

pipe(ioe.tryCatch(()=>5,()=>'error'), ioe.map(num => `${num}`)) // IOEither<'error',string>

它有效,但我不喜欢它结合 monads 和 thunk 类型,原因有两个:

  1. 如果我们实现Tryable了 for Option,thunk 类型仍然是IO. 和其他人一样Either。这就是它开始感觉 hacky、紧密耦合OptionEitherIO!

  2. 这只是一个幸运的巧合,这要归功于鸭子Lazy<A>类型IO<A>与. 如果这要改变,这个解决方案就行不通了。Lazy<Promise<A>>Task<A>

于 2020-12-03T18:15:15.437 回答