In [1]:
{-# LANGUAGE ImplicitParams #-}
In [2]:
data DatabaseService = DatabaseService { getData :: IO [String]}
type NameFilterService = String -> Bool
type NameGetterService = IO [String]
-- Notice, that there are no type definition, since we should define all implicit parameters type
getNames = filter ?nameFilterService <$> getData ?database
-- Notice that there is no knowledge in this layer about the creation of the services
concatenateNames = concat <$> ?getNames
In [3]:
import Data.Char(isUpper)
readingFromFile :: IO [String]
readingFromFile = return ["kutya", "Andy", "macska", "James"]
createGetNames = let ?nameFilterService = isUpper . head
in getNames
main = do let ?database = DatabaseService {getData=readingFromFile}
let ?getNames = createGetNames
concatenatedNames <- concatenateNames
print concatenatedNames
main
In [4]:
data Result = PASSED | FAILED deriving(Show)
assert a = if a then return PASSED else return FAILED
test = mapM_ (>>=print) [ createTestCase ["Kakas","kutya","barany"], createTestCase []
, testConcat [] [], testConcat ["A", "B"] "AB"]
createTestCase test_data = do let ?database = DatabaseService {getData=return test_data}
?nameFilterService = const True
names <- getNames
assert (names == test_data)
-- Notice that we only have to mock one service, which in production uses much more
testConcat input output = do let ?getNames = return input
result <- concatenateNames
assert (result == output)
test