限制效果,如 `Freer`,使用 MTL 风格
Limiting effects, like with `Freer`, using MTL-style
动机:能够像在 Free
/Freer
风格中那样控制 MTL 中的效果。
这个例子可能有点做作——想象一个有一些基本操作的程序(GHC 8.2 使用 freer-simple
),
#!/usr/bin/env stack
-- stack --resolver lts-10.2 --install-ghc runghc --package freer-simple
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
import Control.Monad.Freer
data Effect next where
ReadFilename :: String -> Effect String
WriteOutput :: Show a => a -> Effect ()
Computation :: Int -> Int -> Effect Int
readFilename :: Member Effect effs => String -> Eff effs String
readFilename = send . ReadFilename
writeOutput :: (Member Effect effs, Show a) => a -> Eff effs ()
writeOutput = send . WriteOutput
computation :: Member Effect effs => Int -> Int -> Eff effs Int
computation i1 i2 = send $ Computation i1 i2
有了这个,我们可以建模一个程序,做一些简单的操作,读取文件并输出,进行计算并输出,
program :: Eff '[Effect, IO] ()
program = do
contents <- readFilename "test.txt"
writeOutput contents
result <- computation 12 22
writeOutput result
然后我们可以实现我们的解释器,这将是我们决定如何 运行 代码。我们的第一个解释器会放在客户端,在本地会运行IO
指令,它会将纯指令发送到服务器,进行计算,
runClientEffect :: Eff '[Effect, IO] a -> IO a
runClientEffect = runM . interpretM (\case
ReadFilename filename -> readFile filename
WriteOutput s -> print s
Computation i1 i2 -> do
print "Imagine the networking happening here"
pure $ i1 + i2)
我们现在可以跳过服务器端。
这有希望展示的是一个接口,它依赖于某些方面是纯的,因此能够发送到服务器,而其他方面是不纯的,并且是 运行 本地。
我纠结的是如何以 MTL 风格做到这一点,即如何限制在 monad 操作中可以完成的 IO
数量。
如果问题太模糊,请告诉我!
这很简单。
import Control.Monad.IO.Class
我们定义了一个 EffectMonad
类型类来抽象 monad 本身,而不是 Effect
数据类型来表示我们的 DSL 的语法。
class Monad m => EffectMonad m where
readFilename :: String -> m String
writeOutput :: Show a => a -> m ()
computation :: Int -> Int -> m Int
程序相同(类型签名不同)。
program :: EffectMonad m => m ()
program = do
contents <- readFilename "test.txt"
writeOutput contents
result <- computation 12 22
writeOutput result
解释器作为一个实例给出(如果您正在使用转换器分层效果,这就是我们 运行 进入 O(n*m)
实例问题的地方)。
instance EffectMonad IO where
readFilename filename = readFile filename
writeOutput s = print s
computation i1 i2 = do
print "Imagine the networking happening here"
pure $ i1 + i2
然后 运行程序只是用正确的类型实例化它。
main :: IO ()
main = program
动机:能够像在 Free
/Freer
风格中那样控制 MTL 中的效果。
这个例子可能有点做作——想象一个有一些基本操作的程序(GHC 8.2 使用 freer-simple
),
#!/usr/bin/env stack
-- stack --resolver lts-10.2 --install-ghc runghc --package freer-simple
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
import Control.Monad.Freer
data Effect next where
ReadFilename :: String -> Effect String
WriteOutput :: Show a => a -> Effect ()
Computation :: Int -> Int -> Effect Int
readFilename :: Member Effect effs => String -> Eff effs String
readFilename = send . ReadFilename
writeOutput :: (Member Effect effs, Show a) => a -> Eff effs ()
writeOutput = send . WriteOutput
computation :: Member Effect effs => Int -> Int -> Eff effs Int
computation i1 i2 = send $ Computation i1 i2
有了这个,我们可以建模一个程序,做一些简单的操作,读取文件并输出,进行计算并输出,
program :: Eff '[Effect, IO] ()
program = do
contents <- readFilename "test.txt"
writeOutput contents
result <- computation 12 22
writeOutput result
然后我们可以实现我们的解释器,这将是我们决定如何 运行 代码。我们的第一个解释器会放在客户端,在本地会运行IO
指令,它会将纯指令发送到服务器,进行计算,
runClientEffect :: Eff '[Effect, IO] a -> IO a
runClientEffect = runM . interpretM (\case
ReadFilename filename -> readFile filename
WriteOutput s -> print s
Computation i1 i2 -> do
print "Imagine the networking happening here"
pure $ i1 + i2)
我们现在可以跳过服务器端。
这有希望展示的是一个接口,它依赖于某些方面是纯的,因此能够发送到服务器,而其他方面是不纯的,并且是 运行 本地。
我纠结的是如何以 MTL 风格做到这一点,即如何限制在 monad 操作中可以完成的 IO
数量。
如果问题太模糊,请告诉我!
这很简单。
import Control.Monad.IO.Class
我们定义了一个 EffectMonad
类型类来抽象 monad 本身,而不是 Effect
数据类型来表示我们的 DSL 的语法。
class Monad m => EffectMonad m where
readFilename :: String -> m String
writeOutput :: Show a => a -> m ()
computation :: Int -> Int -> m Int
程序相同(类型签名不同)。
program :: EffectMonad m => m ()
program = do
contents <- readFilename "test.txt"
writeOutput contents
result <- computation 12 22
writeOutput result
解释器作为一个实例给出(如果您正在使用转换器分层效果,这就是我们 运行 进入 O(n*m)
实例问题的地方)。
instance EffectMonad IO where
readFilename filename = readFile filename
writeOutput s = print s
computation i1 i2 = do
print "Imagine the networking happening here"
pure $ i1 + i2
然后 运行程序只是用正确的类型实例化它。
main :: IO ()
main = program