限制效果,如使用"Freer",使用MTL风格

Teh*_*nix 1 haskell monad-transformers free-monad

动机:能够在Free/ Freer-style中控制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
Run Code Online (Sandbox Code Playgroud)

有了这个,我们可以建模一个程序,它做一些简单的操作,读取文件并输出它,进行计算并输出它,

program :: Eff '[Effect, IO] ()
program = do
 contents <- readFilename "test.txt"
 writeOutput contents
 result <- computation 12 22
 writeOutput result
Run Code Online (Sandbox Code Playgroud)

然后我们可以实现我们的解释器,我们将决定如何运行代码.我们的第一个解释器将被放置在客户端,并将在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)
Run Code Online (Sandbox Code Playgroud)

服务器端我们现在可以跳过.

这有希望展示的是一个接口,它依赖于某些方面是纯粹的,从而能够被发送到服务器,而其他方面是不纯的,并且在本地运行.

我挣扎的是,如何以MTL风格实现这一点,即如何限制IOmonad操作中可以完成的数量.

如果问题太模糊,请告诉我!

Li-*_*Xia 5

这很简单.

import Control.Monad.IO.Class
Run Code Online (Sandbox Code Playgroud)

Effect我们定义了一个EffectMonad抽象monad本身的类型类,而不是代表我们DSL语法的数据类型.

class Monad m => EffectMonad m where
  readFilename :: String -> m String
  writeOutput :: Show a => a -> m ()
  computation :: Int -> Int -> m Int
Run Code Online (Sandbox Code Playgroud)

程序是相同的(直到类型签名).

program :: EffectMonad m => m ()
program = do
  contents <- readFilename "test.txt"
  writeOutput contents
  result <- computation 12 22
  writeOutput result
Run Code Online (Sandbox Code Playgroud)

并且给出了一个解释器作为实例(如果你使用变换器分层效果,那就是我们遇到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
Run Code Online (Sandbox Code Playgroud)

然后运行程序只是用正确的类型实例化它.

main :: IO ()
main = program
Run Code Online (Sandbox Code Playgroud)