TaskT를 Trampoline의 모나드 인스턴스와 결합하여 스택리스 비동기 계산을 얻는 방법은 무엇입니까?
Trampoline
모나드이며 모나드 변환기 스택에 스택 안전성을 추가합니다. monadRec
모나드 계산의 결과를 제공 하는 특수 인터프리터 ( ) 에 의존하여이를 달성합니다 (실제로는 무료 모나드 패턴의 특수 버전입니다). 이러한 이유로 Trampoline
모나드는 가장 바깥 쪽 모나드, 즉 트랜스포머 스택의 기본 모나드 여야합니다.
다음 설정 TaskT
(기본적 Cont
으로 공유와 함께 사용)에서 모나드 변환기와 Trampoline
기본 모나드가 있습니다.
// TASK
const TaskT = taskt => record(
TaskT,
thisify(o => {
o.taskt = k =>
taskt(x => {
o.taskt = k_ => k_(x);
return k(x);
});
return o;
}));
// Monad
const taskChainT = mmx => fmm =>
TaskT(k =>
mmx.taskt(x =>
fmm(x).taskt(k)));
const taskOfT = x =>
TaskT(k => k(x));
// Transformer
const taskLiftT = chain => mmx =>
TaskT(k => chain(mmx) (k));
// auxiliary functions
const taskAndT = mmx => mmy =>
taskChainT(mmx) (x =>
taskChainT(mmy) (y =>
taskOfT([x, y])));
const delayTaskT = f => ms => x =>
TaskT(k => setTimeout(comp(k) (f), ms, x));
const record = (type, o) => (
o[Symbol.toStringTag] = type.name || type, o);
const thisify = f => f({});
const log = (...ss) =>
(console.log(...ss), ss[ss.length - 1]);
// TRAMPOLINE
const monadRec = o => {
while (o.tag === "Chain")
o = o.fm(o.chain);
return o.tag === "Of"
? o.of
: _throw(new TypeError("unknown trampoline tag"));
};
// tags
const Chain = chain => fm =>
({tag: "Chain", fm, chain});
const Of = of =>
({tag: "Of", of});
// Monad
const recOf = Of;
const recChain = mx => fm =>
mx.tag === "Chain" ? Chain(mx.chain) (x => recChain(mx.fm(x)) (fm))
: mx.tag === "Of" ? fm(mx.of)
: _throw(new TypeError("unknown trampoline tag"));
// MAIN
const foo = x =>
Chain(delayTaskT(x => x) (0) (x)) (Of);
const bar = taskAndT(
taskLiftT(recChain) (foo(1)))
(taskLiftT(recChain) (foo(2))); // yields TaskT
const main = bar.taskt(x => Of(log(x))); // yields Chain({fm, chain: TaskT})
monadRec(main); // yields [TaskT, TaskT] but [1, 2] desired
Trampoline
이벤트 루프 전에 강제 평가가 비동기 작업의 결과를 받기 때문에 이것은 내가 원하는 것이 아닙니다 . 내가 필요한 것은 다른 방법이지만 이미 언급했듯이 TrampolineT
변압기 가 없습니다 . 내가 무엇을 놓치고 있습니까?
답변
이 코드 조각에는 몇 가지 문제가 있습니다.
이슈 # 1 :에 대한 모나드 변압기가 없습니다 IO
(예 Task
)
.NET 용 모나드 변환기가 없다는 것은 잘 알려져 IO
있습니다. [1] 귀하의 TaskT
유형을 모델로 ContT
하고, ContT
실제로 모나드 변압기이다. 그러나을 사용 하여 문제가 발생 TaskT
하는와 같은 비동기 계산을 수행 setTimeout
하고 있습니다.
TaskT
와 유사한 의 정의를 고려하십시오 ContT
.
newtype TaskT r m a = TaskT { taskt :: (a -> m r) -> m r }
따라서 delayTaskT
유형은 (a -> b) -> Number -> a -> TaskT r m b
.
const delayTaskT = f => ms => x =>
TaskT(k => setTimeout(comp(k) (f), ms, x));
그러나 setTimeout(comp(k) (f), ms, x)
유형과 일치하지 않는 시간 초과 ID를 반환합니다 m r
. 주 k => setTimeout(comp(k) (f), ms, x)
유형을 가져야한다 (b -> m r) -> m r
.
사실, m r
연속 k
이 비동기 적 으로 호출 될 때 유형의 값을 활용하는 것은 불가능합니다 . ContT
모나드 변압기는 동기 계산에 사용할 수 있습니다.
그럼에도 불구하고, 우리는 정의 할 수 Task
의 전문 버전으로 Cont
.
newtype Task a = Task { task :: (a -> ()) -> () } -- Task = Cont ()
따라서 Task
모나드 트랜스포머 스택에가있을 때마다 항상베이스에있을 것 IO
입니다.
Task
모나드 스택을 안전하게 만들고 싶다면 다음 답변 을 읽으십시오 .
문제 # 2 : foo
함수에 잘못된 반환 유형이 있습니다.
잠시 delayTaskT
동안 올바른 유형이 있다고 가정 해 봅시다 . 이미 알고 있듯이 다음 문제 foo
는 잘못된 반환 유형 이 있다는 것입니다.
문제는
foo
a로TaskT
래핑 된 것을 반환하는 것으로 보이며이Chain
래핑TaskT
은TaskT
체인 에서 완전히 분리 되어 절대로 평가 / 발사되지 않습니다.
나는 예상되는 유형 foo
이 a -> TaskT r Trampoline a
. 그러나의 실제 유형은 foo
입니다 a -> Trampoline (TaskT r m a)
. 다행히도 수정은 쉽습니다.
const foo = delayTaskT(x => x) (0);
의 유형 foo
과 동일합니다 taskOfT
즉 a -> TaskT r m a
. 우리는 전문화 할 수 있습니다 m = Trampoline
.
문제 # 3 : taskLiftT
올바르게 사용하고 있지 않습니다.
이 taskLiftT
함수는 기본 모나드 계산을 TaskT
계층 으로 들어 올립니다 .
taskLiftT :: (forall a b. m a -> (a -> m b) -> m b) -> m a -> TaskT r m a
taskLiftT(recChain) :: Trampoline a -> TaskT r Trampoline a
지금, 당신은 적용하고 taskLiftT(recChain)
에 foo(1)
와 foo(2)
.
foo :: a -> Trampoline (TaskT r m a) -- incorrect definition of foo
foo(1) :: Trampoline (TaskT r m Number)
foo(2) :: Trampoline (TaskT r m Number)
taskLiftT(recChain) (foo(1)) :: TaskT r Trampoline (TaskT r m Number)
taskLiftT(recChain) (foo(2)) :: TaskT r Trampoline (TaskT r m Number)
그러나 올바른 정의를 사용 foo
하면 유형이 일치하지 않을 것입니다.
foo :: a -> TaskT r Trampoline a -- correct definition of foo
foo(1) :: TaskT r Trampoline Number
foo(2) :: TaskT r Trampoline Number
-- Can't apply taskLiftT(recChain) to foo(1) or foo(2)
의 올바른 정의를 사용하는 경우을 정의 foo
하는 두 가지 방법이 bar
있습니다. 을 foo
사용하여 올바르게 정의 할 수있는 방법은 없습니다 setTimeout
. 따라서, 나는 재정의 한 foo
것처럼 taskOfT
.
사용
foo
하고 사용 하지 마십시오taskLiftT
.const bar = taskAndT(foo(1))(foo(2)); // yields TaskT
// TASK const TaskT = taskt => record( TaskT, thisify(o => { o.taskt = k => taskt(x => { o.taskt = k_ => k_(x); return k(x); }); return o; })); // Monad const taskChainT = mmx => fmm => TaskT(k => mmx.taskt(x => fmm(x).taskt(k))); const taskOfT = x => TaskT(k => k(x)); // Transformer const taskLiftT = chain => mmx => TaskT(k => chain(mmx) (k)); // auxiliary functions const taskAndT = mmx => mmy => taskChainT(mmx) (x => taskChainT(mmy) (y => taskOfT([x, y]))); const delayTaskT = f => ms => x => TaskT(k => setTimeout(comp(k) (f), ms, x)); const record = (type, o) => ( o[Symbol.toStringTag] = type.name || type, o); const thisify = f => f({}); const log = (...ss) => (console.log(...ss), ss[ss.length - 1]); // TRAMPOLINE const monadRec = o => { while (o.tag === "Chain") o = o.fm(o.chain); return o.tag === "Of" ? o.of : _throw(new TypeError("unknown trampoline tag")); }; // tags const Chain = chain => fm => ({tag: "Chain", fm, chain}); const Of = of => ({tag: "Of", of}); // Monad const recOf = Of; const recChain = mx => fm => mx.tag === "Chain" ? Chain(mx.chain) (x => recChain(mx.fm(x)) (fm)) : mx.tag === "Of" ? fm(mx.of) : _throw(new TypeError("unknown trampoline tag")); // MAIN const foo = taskOfT; const bar = taskAndT(foo(1))(foo(2)); // yields TaskT const main = bar.taskt(x => Of(log(x))); // yields Chain({fm, chain: TaskT}) monadRec(main); // yields [TaskT, TaskT] but [1, 2] desired
사용하지 마십시오
foo
사용taskLiftT
.const bar = taskAndT( taskLiftT(recChain) (Of(1))) (taskLiftT(recChain) (Of(2))); // yields TaskT
// TASK const TaskT = taskt => record( TaskT, thisify(o => { o.taskt = k => taskt(x => { o.taskt = k_ => k_(x); return k(x); }); return o; })); // Monad const taskChainT = mmx => fmm => TaskT(k => mmx.taskt(x => fmm(x).taskt(k))); const taskOfT = x => TaskT(k => k(x)); // Transformer const taskLiftT = chain => mmx => TaskT(k => chain(mmx) (k)); // auxiliary functions const taskAndT = mmx => mmy => taskChainT(mmx) (x => taskChainT(mmy) (y => taskOfT([x, y]))); const delayTaskT = f => ms => x => TaskT(k => setTimeout(comp(k) (f), ms, x)); const record = (type, o) => ( o[Symbol.toStringTag] = type.name || type, o); const thisify = f => f({}); const log = (...ss) => (console.log(...ss), ss[ss.length - 1]); // TRAMPOLINE const monadRec = o => { while (o.tag === "Chain") o = o.fm(o.chain); return o.tag === "Of" ? o.of : _throw(new TypeError("unknown trampoline tag")); }; // tags const Chain = chain => fm => ({tag: "Chain", fm, chain}); const Of = of => ({tag: "Of", of}); // Monad const recOf = Of; const recChain = mx => fm => mx.tag === "Chain" ? Chain(mx.chain) (x => recChain(mx.fm(x)) (fm)) : mx.tag === "Of" ? fm(mx.of) : _throw(new TypeError("unknown trampoline tag")); // MAIN const foo = taskOfT; const bar = taskAndT( taskLiftT(recChain) (Of(1))) (taskLiftT(recChain) (Of(2))); // yields TaskT const main = bar.taskt(x => Of(log(x))); // yields Chain({fm, chain: TaskT}) monadRec(main); // yields [TaskT, TaskT] but [1, 2] desired
[1] Haskell에 IO 변환기가없는 이유는 무엇입니까?