Last active
August 10, 2021 18:25
-
-
Save andyfriesen/43d886ce60927c69b3d1 to your computer and use it in GitHub Desktop.
Tiny example showing how to force your Haskell code to be 100% deterministic under unit tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# LANGUAGE FlexibleInstances #-} | |
module Main where | |
import Control.Applicative ((<$>)) | |
import Control.Monad.State.Lazy as S | |
class Monad m => World m where | |
writeLine :: String -> m () | |
instance World IO where | |
writeLine = putStrLn | |
data FakeState = FS { fsWrittenLines :: [String] } | |
deriving (Show) | |
def :: FakeState | |
def = FS { fsWrittenLines = [] } | |
instance World (S.State FakeState) where | |
writeLine s = do | |
oldLines <- fsWrittenLines <$> S.get | |
S.put FS { fsWrittenLines = s:oldLines } | |
runFakeWorld :: b -> State b a -> (a, b) | |
runFakeWorld = flip S.runState | |
importantBusinessAction :: World m => m String | |
importantBusinessAction = do | |
writeLine "This action is prohibited from doing any untestable IO." | |
writeLine "Writing to stdout is testable, though, so this is all ok." | |
return "Pickles" | |
main :: IO () | |
main = do | |
_ <- importantBusinessAction | |
let (result, endState) = runFakeWorld def importantBusinessAction | |
print endState | |
print result |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment