Мотивация: Чтобы иметь возможность управлять эффектами в MTL, как мы можем в стиле Free
/Freer
.
Пример может быть немного надуманным — представьте себе программу с некоторыми базовыми операциями (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, а именно, как ограничить количество IO
, которое можно выполнить в монадных операциях.
Дайте мне знать, если вопрос слишком расплывчатый!