[英]Represent Task[Either] and IO[Either] as a single monad that includes tryCatch?
有沒有辦法將 IOEither 和 TaskEither 表示為一個也包括tryCatch
的 Monad?
我目前將在 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! */)
到目前為止一切都很好,但是,雖然有一個用於 Option、Either、 tryCatch
、IOEither 等的 tryCatch,但我可以說它不是任何接口的一部分。 所以我正在嘗試創建自己的:
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 並放棄無標記決賽的步驟?
我有一個臨時的解決方案,感覺很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 類型,原因有兩個:
如果我們為Option
實現Tryable
,則 thunk 類型仍然是IO
。 對於Either
和其他人也是如此。 這就是它開始感覺 hacky 的地方,將Option
和Either
與IO
緊密耦合!
這只是一個幸運的巧合,這要歸功於鴨子類型Lazy<A>
與IO<A>
的類型相同,而Lazy<Promise<A>>
與Task<A>
的類型相同。 如果這要改變,這個解決方案就行不通了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.