Mocking IO Actions: getArgs and putStrLn - unit-testing

I'm trying to test a small function (or rather, IO Action) that takes a command line argument and outputs it to the screen. My original (untestable) function is:
-- In Library.hs
module Library where
import System.Environment (getArgs)
run :: IO ()
run = do
args <- getArgs
putStrLn $ head args
After looking at this answer about mocking, I have come up with a way to mock getArgs and putStrLn by using a type class constrained type. So the above function becomes:
-- In Library.hs
module Library where
class Monad m => SystemMonad m where
getArgs :: m [String]
putStrLn :: String -> m ()
instance SystemMonad IO where
getArgs = System.Environment.getArgs
putStrLn = Prelude.putStrLn
run :: SystemMonad m => m ()
run = do
args <- Library.getArgs
Library.putStrLn $ head args
This Library., Prelude. and System.Environment. are to avoid compiler complaints of Ambigious Occurence. My test file looks like the following.
-- In LibrarySpec.hs
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
import Library
import Test.Hspec
import Control.Monad.State
data MockArgsAndResult = MockArgsAndResult [String] String
deriving(Eq, Show)
instance SystemMonad (State MockArgsAndResult) where
getArgs = do
MockArgsAndResult args _ <- get
return args
putStrLn string = do
MockArgsAndResult args _ <- get
put $ MockArgsAndResult args string
return ()
main :: IO ()
main = hspec $ do
describe "run" $ do
it "passes the first command line argument to putStrLn" $ do
(execState run (MockArgsAndResult ["first", "second"] "")) `shouldBe` (MockArgsAndResult ["first", "second"] "first")
I'm using a State monad that effectively contains 2 fields.
A list for the command line arguments where the mock getArgs reads from
A string that the mock putStrLn puts what was passed to it.
The above code works and seems to test what I want it to test. However, I'm wondering if there is some better / cleaner / more idiomatic way of testing this. For one thing, I'm using the same state to both put stuff into the test (my fake command line arguments), and then get stuff out of it (what was passed to putStrLn.
Is there a better way of doing what I'm doing? I'm more familiar with mocking in a Javascript environment, and my knowledge of Haskell is pretty basic (I arrived at the above solution by a fair bit of trial and error, rather than actual understanding)

The better way is to avoid needing to provide mock versions of getArgs and putStrLn by separating out the heart of the computation into a pure function.
Consider this example:
main = do
args <- getArgs
let n = length $ filter (\w -> length w < 5) args
putStrLn $ "Number of small words: " ++ show n
One could say that the heart of the computation is counting the number of small words which is a pure function of type [String] -> Int. This suggest that we should refactor the program like this:
main = do
args <- getArgs
let n = countSmallWords args
putStrLn $ "Number of small words: " ++ show n
countSmallWords :: [String] -> Int
countSmallWords ws = ...
Now we just test countSmallWords, and this is easy because it is pure function.

Related

How to add to a List from IO in Haskell.

I am trying to write a simple function which reads in one line at a time (which I know will be integers) and then stores them in to a list. For the life of me however, it seems like the list comes out to empty.
import System.IO
import Control.Monad
type Int2 = [Int]
valueTab = [] :: [Int]
app [ ] list = list
app (h:t) list = h:(app t list)
main :: IO ()
main = do
hSetBuffering stdout NoBuffering -- DO NOT REMOVE
-- Auto-generated code below aims at helping you parse
-- the standard input according to the problem statement.
input_line <- getLine
let n = read input_line :: Int
let value = [] :: [Int]
replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int
hPutStrLn stderr (show input_line)
hPutStrLn stderr (show valueTab)
return $ app valueTab [pi]
-- hPutStrLn stderr "Debug messages..."
-- Write answer to stdout
--putStrLn input_line
return ()
So when I run this with
8
6
4
3 all on their own lines,
It prints 6, [], 4, [], 3 [].
Is this a problem with my printing, with my list declaration, with the way that I store them or? I have both value and valueTab to check whether it was a scope problem.
Note: The code is boilerplate code on a coding website that tests it on it's platform. Assume replicateM is just a loop that runs through the code x number of times.
It looks like you're doing a problem on codinggame.com. Other coding sites seem to do a better job with their Haskell templates, particularly for beginner exercises -- the template usually takes care of all the input and output, and you just need to supply the missing pure function. In contrast, codinggame.com's Haskell templates seem to assume the beginner has a pretty firm grasp of the IO monad, and leave out a lot of essential details (e.g., the fact that the result of the replicateM action should actually be bound to a variable) which you're expected to fill in.
The replicateM call in the original template probably looked something like:
replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int -- maybe this line was there, maybe not
return ()
This expression creates a composite IO action that, when executed, will repeat the following subaction n times:
read a line, binding the string read to input_line
prepare to convert that line to an integer value pi (though this is never done because pi isn't used)
regardless of the string read, return "unit" (the value ()) as the result of the subaction
The value of the composite action is then a list of the values returned by the subactions. Since those were all units, the final value of the composite action is a list [(),(),()...()], with one () for each line read. However, because the value of this composite action is never bound to a variable (i.e., because there's no result <- ... before the replicateM expression), this list is thrown away.
So, this template provides a needlessly verbose method of reading n lines of input and doing nothing with them.
Fortunately, all you need to do to make this template do something useful is to have the subaction return a value that's useful (e.g., the integer pi) which will cause the composite action to return a list of the integers read, and then make sure you bind the resulting list to a variable using the <- notation.
In other words, you want to write:
main = do
...
pis <- replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int
return pi
hPutStrLn stderr (show pis)
You won't need the helper function app, and you won't need to pre-declare a list valueTab to contain the result. The result is automatically produced by replicateM, and you just need to name it so you can use it.
The complete working program will look like:
import System.IO
import Control.Monad
type Int2 = [Int]
main :: IO ()
main = do
hSetBuffering stdout NoBuffering -- DO NOT REMOVE
-- Auto-generated code below aims at helping you parse
-- the standard input according to the problem statement.
input_line <- getLine
let n = read input_line :: Int
let value = [] :: [Int]
pis <- replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int
return pi
hPutStrLn stderr (show pis)
-- hPutStrLn stderr "Debug messages..."
-- Write answer to stdout
return ()

Inserting a regexp() SQLite function in Haskell (Database.SQLite3,"direct-sqlite")

I need to create an implementation of the regex() SQLite function in a Haskell database connection so that I can use the "REGEX" operator in queries.
Now, I have an implementation of a regex matching function that uses PCRE:
import Text.Regex.Base.RegexLike
import qualified Text.Regex.PCRE.ByteString as PCRE
import qualified Data.ByteString as BS
sqlRegex :: BS.ByteString -> BS.ByteString -> IO Bool
sqlRegex reg b = do
reC <- pcreCompile reg
re <- case reC of
(Right r) -> return r
reE <- PCRE.execute re b
case reE of
(Right (Just _)) -> return True
(Right (Nothing)) -> return False
where pcreCompile = PCRE.compile defaultCompOpt defaultExecOpt
which works well (please excuse the very explicit calls)
> sqlRegex (Data.ByteString.Char8.pack ".*") (Data.ByteString.Char8.pack "hello")
True
> sqlRegex (Data.ByteString.Char8.pack "H.*") (Data.ByteString.Char8.pack "hello")
False
Now, how do I create the SQLite function??
conn <- open $ pack dbFile
createFunction conn "regexp" (Just 2) True [..... and what should go here?]
The docs for createFunction
helps me as far as making me understand that I need to make the function take a context and some arguments, but the refs for those data does not help me at all!
How should make my function take a FuncContext and FuncArgs??
There is an example in the github repo:
https://github.com/IreneKnapp/direct-sqlite/blob/master/test/Main.hs#L743-757
-- implements repeat(n,str)
repeatString ctx args = do
n <- funcArgInt64 args 0
s <- funcArgText args 1
funcResultText ctx $ T.concat $ replicate (fromIntegral n) s
You use the functions funcArg... to get the arguments and functions like funcResult... to return them.
Links to the docs:
Extract Function Arguments
Set the Result of a Function

prompt user to build a string list

I would like to build a string list by prompting the user for input. My end goal is to be able to parse a string list against a simple hash table using a simple routine.
`let list_find tbl ls =
List.iter (fun x ->
let mbr = if Hashtbl.mem tbl x then "aok" else "not found"
in
Printf.printf "%s %s\n" x mbr) ls ;;`
Building a string list is accomplished with the cons operator ::, but somehow I am not able to get the prompt to generate a string list. A simpe list function returns anything that is put into it as a list:
`let build_strlist x =
let rec aux x = match x with
| [] -> []
| hd :: tl -> hd :: aux tl
in
aux x ;;`
Thus far, I have been able to set the prompt, but building the string list did not go so well. I am inclined to think I should be using Buffer or Scanning.in_channel. This is what I have thus far:
`#load "unix.cma" ;;
let prompt () = Unix.isatty Unix.stdin && Unix.isatty Unix.stdout ;;
let build_strlist () =
let rec loop () =
let eof = ref false in
try
while not !eof do
if prompt () then print_endline "enter input ";
let line = read_line () in
if line = "-1" then eof := true
else
let rec build x = match x with
| [] -> []
| hd :: tl -> hd :: build tl
in
Printf.printf "you've entered %s\n" (List.iter (build line));
done
with End_of_file -> ()
in
loop () ;;`
I am getting an error the keyword "line" has the type string, but an expression was expected of type 'a list. Should I be building the string list using Buffer.create buf and then Buffer.add_string buf prepending [ followed by quotes " another " and a semicolon? This seems to be an overkill. Maybe I should just return a string list and ignore any attempts to "peek at what we have"? Printing will be done after checking the hash table.
I would like to have a prompt routine so that I can use ocaml for scripting and user interaction. I found some ideas on-line which allowed me to write the skeleton above.
I would probably break down the problem in several steps:
get the list of strings
process it (in your example, simply print it back)
1st step can be achieved with a recursive function as follow:
let build_strlist' () =
let rec loop l =
if prompt () then (
print_string "enter input: ";
match read_line () with
"-1" -> l
| s -> loop (s::l)
) else l
in loop [];;
See how that function loops on itself and build up the list l as it goes. As you mentioned in your comment, I dropped the imperative part of your code to keep the functional recursion only. You could have achieved the same by keeping instead the imperative part and leaving out the recursion, but recursion feels more natural to me, and if written correctly, leads to mostly the same machine code.
Once you have the list, simply apply a List.iter to it with the ad hoc printing function as you did in your original function.

What role does the indentation play here? and Why one indent doesn't work?

Here is a sample program from RWH book. I'm wondering why the first works great but the second can't even compile? The only difference is the first one uses 2 tabs after where mainWith func = do whereas the second uses only 1. Not sure what difference does that mean? Why the second fails to compile? And also why do construct can be empty?
Thanks a lot,
Alex
-- Real World Haskell Sample Code Chapter 4:
-- http://book.realworldhaskell.org/read/functional-programming.html
import System.Environment (getArgs)
interactWith func input output = do
s <- readFile input
writeFile output (func s)
main = mainWith myFunction
where mainWith func = do
args <- getArgs
case args of
[fin, fout] -> do
interactWith func fin fout
_ -> putStrLn "error: exactly two arguments needed"
myFunction = id
-- The following code has a compilation error
-- % ghc --make interactWith.hs
-- [1 of 1] Compiling Main ( interactWith.hs, interactWith.o )
--
-- interactWith.hs:8:26: Empty 'do' construct
import System.Environment (getArgs)
interactWith func input output = do
s <- readFile input
writeFile output (func s)
main = mainWith myFunction
where mainWith func = do
args <- getArgs
case args of
[fin, fout] -> do
interactWith func fin fout
_ -> putStrLn "error: exactly two arguments needed"
myFunction = id
The definition of the mainWith function is indented to column 10:
where mainWith func = do
^
The contents of the do block started in this line are only indented to column 8:
args <- getArgs
case args of
...
^
If you increase the indentation of the contents of the do block to be also indented at least to column 10, the code is parsed correctly. With the current indentation the lines that should belong to the do block are seen to be part of the where clause, but not the mainWith function.
The do-block can not be empty, that's why you get the error. When using only one tab args <- getArgs is seen as part of the where-block, not the do-block, so the do-block is empty and you get an error.
The thing is that unless you use {} and ; to explicitly state which block goes from where to where, haskell relies on indendation. And since you indented your line only by one level, it was seen as part of the where-block.

Can you turn a Haskell list into a series of do instructions?

Can you create a list of functions and then execute them sequentially, perhaps passing them into do notation?
I'm currently doing this by mapping over a list of data and am wondering if I can call somehow pass the result as a series of sequential calls?
Something like this?
sequence [putStrLn "Hello", putStrLn "World"]
If these are functions, ie pure, then you can use ($) or "apply":
execute functions argument = map ($argument) functions
-- execute [id,(1+),(1-)] 12 => [12,13,-11]
There's no guarantee that this happens sequentially of course, but you'll get a list of the return values.
If these are actions, ie impure, then what you want is called sequence_:
sequence_ [putStr "Hello", putStr " world", putStrLn "!"]
sequence_ is pretty easy to write yourself:
sequence_ [] = return ()
sequence_ (action:actions) = action >> sequence_ actions
There is also a sequence (without the underscore) that runs a bunch of actions and returns their results:
main = do
ss <- sequence [readFile "foo.txt", readFile "bar.txt", readFile "baz.txt"]
-- or ss <- mapM readFile ["foo.txt", "bar.txt", "baz.txt"]
good answers so far, but if you also want each function to act not on the original data but on the result of the previous function, look at the foldding functions, such as foldl, foldl1, and foldr:
fns = [(1-), (+2), (abs), (+1)]
helperFunction a f = f a
test1 n = foldl helperFunction n fns
and you may need the monadic version, foldM and foldM_ :
import Control.Monad
import Data.Char
helperFunction a f = f a
prnt = \s-> do putStrLn s; return s
actions = [return, prnt, return.reverse, prnt, return.(map toUpper), prnt, return.reverse, prnt]
test2 str = foldM_ helperFunction str actions