Ответ 1
Тест для программы Free f a
- это просто интерпретатор для программы Free f a -> r
, создающий некоторый результат r
То, что вы ищете, - это простой способ создания интерпретаторов для программы, которые утверждают, что результат программы - это то, что вы ожидали. Каждый шаг интерпретатора либо разворачивает инструкцию Free f
из программы, либо описывает некоторую ошибку. Они будут иметь тип
Free DSL a -> Either String (Free DSL a)
| | ^ the remaining program after this step
| ^ a descriptive error
^ the remaining program before this step
Мы проведем тест для каждого из конструкторов в DSL
. prompt'
ожидает a Prompt
с определенным значением и предоставляет значение ответа функции для поиска следующего.
prompt' :: String -> String -> Free DSL a -> Either String (Free DSL a)
prompt' expected response f =
case f of
Free (Prompt p cont) | p == expected -> return (cont response)
otherwise -> Left $ "Expected (Prompt " ++ show expected ++ " ...) but got " ++ abbreviate f
abbreviate :: Free DSL a -> String
abbreviate (Free (Prompt p _)) = "(Free (Prompt " ++ show p ++ " ...))"
abbreviate (Free (Display p _)) = "(Free (Display " ++ show p ++ " ...))"
abbreviate (Pure _) = "(Pure ...)"
display'
ожидает Display
с определенным значением.
display' :: String -> Free DSL a -> Either String (Free DSL a)
display' expected f =
case f of
Free (Display p next) | p == expected -> return next
otherwise -> Left $ "Expected (Display " ++ show expected ++ " ...) but got " ++ abbreviate f
pure'
ожидает a Pure
со значением
pure' :: (Eq a, Show a) => a -> Free DSL a -> Either String ()
pure' expected f =
case f of
Pure a | a == expected -> return ()
otherwise -> Left $ "Expected " ++ abbreviate' (Pure expected) ++ " but got " ++ abbreviate' f
abbreviate' :: Show a => Free DSL a -> String
abbreviate' (Pure a) = "(Pure " ++ showsPrec 10 a ")"
abbreviate' f = abbreviate f
С prompt'
и display'
мы можем легко построить интерпретатор в стиле expect
.
expect :: Free DSL a -> Either String (Free DSL a)
expect f = return f >>=
prompt' "Enter your name:" "radix" >>=
display' "Why hello there, radix." >>=
prompt' "And what is your friend name?" "Bob" >>=
display' "It good to meet you too, Bob."
Выполнение этого теста
main = either putStrLn (putStrLn . const "Passed") $ expect greet
Результаты с отказом
Expected (Prompt "Enter your name:" ...) but got (Free (Prompt "Enter your name: " ...))
Как только мы изменим тест, чтобы ожидать пробелы в конце подсказок
expect :: Free DSL a -> Either String (Free DSL a)
expect f = return f >>=
prompt' "Enter your name: " "radix" >>=
display' "Why hello there, radix." >>=
prompt' "And what is your friend name? " "Bob" >>=
display' "It good to meet you too, Bob."
Выполнение этого результата приводит к
Passed