Работя по проект, който изисква от меннапишете малък преводач. Инструкциите имат проста дървовидна структура и една от командите има ефект на спиране на изпълнението. Така в примера по-долу "baz" никога не се отпечатва.
import Control.Monad.Cont
data Instruction = Print String | Halt | Block [Instruction]
deriving (Eq, Show)
instructions =
[ Print "foo"
, Block
[ Print "bar"
, Halt
]
, Print "baz"
]
main :: IO ()
main = runContT (callCC $ interpret instructions)
(const $ pure ())
interpret [] k = pure ()
interpret (a:as) k = case a of
Print str -> liftIO (putStrLn str) >> interpret as k
Block ins -> interpret ins k >> interpret as k
Halt -> k ()
За първи път съм виждал потенциална употреба ContT
в един от моите проекти. Чудех се дали това е подходящо използване от него или ако има по-просто решение, което може да се пренебрегне.
Отговори:
7 за отговор № 1Да, това изглежда е точно такъв случай на използване Control.Monad.Cont
е подходящо.
Почти сигурно сте наясно с това, но за други читатели си струва да се каже, че ако сте написали interpret
така, че да е функция от списък с инструкции към IO ()
така:
main :: IO ()
main = interpret instructions
interpret :: [Instruction] -> IO ()
interpret [] = pure ()
interpret (a:as) = case a of
Print str -> putStrLn str >> interpret as
Block ins -> interpret ins >> interpret as
Halt -> pure ()
тогава foo
bar
и baz
всички щяха да печатат. Това, от което се нуждаете, е механизъм за бягство, който ще ви позволи да прекъснете цялото изчисление и веднага да върнете стойност. Точно това е callCC
осигурява. Извикване на споменатото изчисление (k
във вашия код) ви позволява да избягате от цялото изчисление, а не само това ниво / слой.
Толкова добра работа, намерих точно подходящия случай на използване на ContT тук, аз вярвам.