根据另一个具有更自由的可扩展效果的编码效果
Encode effect in terms of another with freer extensible effects
我一直在玩 “freer monads” and extensible effects, implemented in the freer-effects 包,我 运行 遇到了一个看似可行但我无法解决的问题。
我写了一个表示与文件系统的简单交互的类型:
data FileSystem v where
ReadFile :: FilePath -> FileSystem String
WriteFile :: FilePath -> String -> FileSystem ()
在 IO
中为此编写解释器很容易,但很无聊。我真正感兴趣的是编写一个在内部使用 State
的纯解释器。我 可以 有效地将 runState
的实现内联到我的 FileSystem
的解释器中,但这似乎有点违背了目的。我 真正 想做的是编写这两种类型之间的转换,然后重用 State
解释器。
编写这样的转换很简单:
fsAsState :: forall v r. FileSystem v -> Eff (State [(FilePath, String)] ': r) v
fsAsState (ReadFile a) = (lookup a <$> get) >>=
maybe (fail "readFile: file does not exist") return
fsAsState (WriteFile a b) = modify $ \fs ->
(a, b) : filter ((/= a) . fst) fs
现在我想要一个通用的 reencode
函数,它可以接受我的 fsAsState
转换并通过重用 State
解释器用它来解释我的 FileSystem
。有了这样的功能,我就可以写出下面的解释器了:
runInMemoryFS :: forall r w. [(FilePath, String)] -> Eff (FileSystem ': r) w -> Eff r (w, [(FilePath, String)])
runInMemoryFS fs m = runState (reencode fsAsState m) fs
棘手的事情实际上是实施 reencode
。我得到了 几乎 类型检查的东西:
reencode :: forall r w f g. (forall v. f v -> Eff (g ': r) v) -> Eff (f ': r) w -> Eff (g ': r) w
reencode f m = loop m
where
loop :: Eff (f ': r) w -> Eff (g ': r) w
loop (Val x) = return x
loop (E u q) = case decomp u of
Right x -> qComp q loop =<< f x
Left u' -> E (weaken u') undefined
不幸的是,我不知道如何在 loop
的最终情况下为 E
提供第二个参数。我不认为我了解优化的 FTCQueue
类型如何工作的实现细节,以便了解我是否需要在这里做一些简单的事情,或者我正在做的事情是不可能的。
这可能吗?如果答案是否定的,事实证明我正在做的事情是,实际上是不可能的,我很想得到一个解释来帮助我理解为什么。
免责声明:以下类型检查,但我还没有尝试 运行。
您需要走 q
(从 E u q
模式匹配)并将其所有步骤从 Eff (f ': r)
移动到 Eff (g ': r)
。我们可以多态地写这个遍历:
shiftQ :: forall m n a b. (forall a. m a -> n a) -> FTCQueue m a b -> FTCQueue n a b
shiftQ shift q = case tviewl q of
TOne act -> tsingleton (shift . act)
act :| q -> go (tsingleton (shift . act)) q
where
go :: forall a b c. FTCQueue n a b -> FTCQueue m b c -> FTCQueue n a c
go q' q = case tviewl q of
TOne act -> q' |> (shift . act)
act :| q -> go (q' |> (shift . act)) q
(有点尴尬,因为我们只能snoc并且只能uncons FTCQueue
s)。
然后我们可以通过将 reencode f
本身作为 shift
er:
在 reencode
中使用它
reencode :: forall r w f g. (forall v. f v -> Eff (g ': r) v) -> Eff (f ': r) w -> Eff (g ': r) w
reencode f m = loop m
where
loop :: Eff (f ': r) w -> Eff (g ': r) w
loop (Val x) = return x
loop (E u q) = case decomp u of
Right x -> qComp q loop =<< f x
Left u' -> E (weaken u') (shiftQ (reencode f) q)
我一直在玩 “freer monads” and extensible effects, implemented in the freer-effects 包,我 运行 遇到了一个看似可行但我无法解决的问题。
我写了一个表示与文件系统的简单交互的类型:
data FileSystem v where
ReadFile :: FilePath -> FileSystem String
WriteFile :: FilePath -> String -> FileSystem ()
在 IO
中为此编写解释器很容易,但很无聊。我真正感兴趣的是编写一个在内部使用 State
的纯解释器。我 可以 有效地将 runState
的实现内联到我的 FileSystem
的解释器中,但这似乎有点违背了目的。我 真正 想做的是编写这两种类型之间的转换,然后重用 State
解释器。
编写这样的转换很简单:
fsAsState :: forall v r. FileSystem v -> Eff (State [(FilePath, String)] ': r) v
fsAsState (ReadFile a) = (lookup a <$> get) >>=
maybe (fail "readFile: file does not exist") return
fsAsState (WriteFile a b) = modify $ \fs ->
(a, b) : filter ((/= a) . fst) fs
现在我想要一个通用的 reencode
函数,它可以接受我的 fsAsState
转换并通过重用 State
解释器用它来解释我的 FileSystem
。有了这样的功能,我就可以写出下面的解释器了:
runInMemoryFS :: forall r w. [(FilePath, String)] -> Eff (FileSystem ': r) w -> Eff r (w, [(FilePath, String)])
runInMemoryFS fs m = runState (reencode fsAsState m) fs
棘手的事情实际上是实施 reencode
。我得到了 几乎 类型检查的东西:
reencode :: forall r w f g. (forall v. f v -> Eff (g ': r) v) -> Eff (f ': r) w -> Eff (g ': r) w
reencode f m = loop m
where
loop :: Eff (f ': r) w -> Eff (g ': r) w
loop (Val x) = return x
loop (E u q) = case decomp u of
Right x -> qComp q loop =<< f x
Left u' -> E (weaken u') undefined
不幸的是,我不知道如何在 loop
的最终情况下为 E
提供第二个参数。我不认为我了解优化的 FTCQueue
类型如何工作的实现细节,以便了解我是否需要在这里做一些简单的事情,或者我正在做的事情是不可能的。
这可能吗?如果答案是否定的,事实证明我正在做的事情是,实际上是不可能的,我很想得到一个解释来帮助我理解为什么。
免责声明:以下类型检查,但我还没有尝试 运行。
您需要走 q
(从 E u q
模式匹配)并将其所有步骤从 Eff (f ': r)
移动到 Eff (g ': r)
。我们可以多态地写这个遍历:
shiftQ :: forall m n a b. (forall a. m a -> n a) -> FTCQueue m a b -> FTCQueue n a b
shiftQ shift q = case tviewl q of
TOne act -> tsingleton (shift . act)
act :| q -> go (tsingleton (shift . act)) q
where
go :: forall a b c. FTCQueue n a b -> FTCQueue m b c -> FTCQueue n a c
go q' q = case tviewl q of
TOne act -> q' |> (shift . act)
act :| q -> go (q' |> (shift . act)) q
(有点尴尬,因为我们只能snoc并且只能uncons FTCQueue
s)。
然后我们可以通过将 reencode f
本身作为 shift
er:
reencode
中使用它
reencode :: forall r w f g. (forall v. f v -> Eff (g ': r) v) -> Eff (f ': r) w -> Eff (g ': r) w
reencode f m = loop m
where
loop :: Eff (f ': r) w -> Eff (g ': r) w
loop (Val x) = return x
loop (E u q) = case decomp u of
Right x -> qComp q loop =<< f x
Left u' -> E (weaken u') (shiftQ (reencode f) q)