I am learnig Haskell, so it's probably something pretty trivial, but I would appreciate some pointers on how to rewrite it and how it works.
I have following working code (used packages: HTF, Parsec and Flow):
{-# OPTIONS_GHC -F -pgmF htfpp #-}
{-# LANGUAGE FlexibleContexts #-}
module Main where
import Test.Framework -- assertEqual, assertBool, htfMain, htf_thisModulesTests
import Text.ParserCombinators.Parsec (eof, spaces, parse)
import Flow ((|>))
import Data.Either (isLeft)
whiteSpaces = spaces
test_parse_whitespace = do
mapM_ positive [
"", " ", "\t", "\n", "\r\n", " \r\n ",
" \t \r\n \t \n \r \t "
]
mapM_ negative ["x", " x", "x ", " x ", "\t_\t"]
where
parser = whiteSpaces >> eof
parseIt = parse parser ""
positive str = assertEqual (parseIt str) (Right ())
negative str = assertBool (parseIt str |> isLeft)
main :: IO ()
main = htfMain htf_thisModulesTests
I am adding a new test which have almost same the where part, so I tried to refactor it like this:
pos_neg_case parser = do
return [positive, negative]
where
fullParser = parser >> eof
parseIt = parse fullParser ""
positive str = assertEqual (parseIt str) (Right ())
negative str = assertBool (parseIt str |> isLeft)
test_parse_whitespace' = do
mapM_ positive [
"", " ", "\t", "\n", "\r\n", " \r\n ",
" \t \r\n \t \n \r \t "
]
mapM_ negative ["x", " x", "x ", " x ", "\t_\t"]
where
[positive, negative] = pos_neg_case whiteSpaces
Which doesn't work (even when I turn the lang. feature on as compiler suggests).
Couldn't match expected type ‘[Char] -> m b0’
with actual type ‘[String -> IO ()]’
Relevant bindings include
test_parse_whitespace' :: m () (bound at test/Spec.hs:21:1)
In the first argument of ‘mapM_’, namely ‘positive’
In a stmt of a 'do' block:
mapM_ positive ["", " ", "\t", "\n", ....]
Couldn't match expected type ‘[Char] -> m b1’
with actual type ‘[String -> IO ()]’
Relevant bindings include
test_parse_whitespace' :: m () (bound at test/Spec.hs:21:1)
In the first argument of ‘mapM_’, namely ‘negative’
In a stmt of a 'do' block:
mapM_ negative ["x", " x", "x ", " x ", ....]
As you have noticed, the problem was the return
you added to:
pos_neg_case parser = do
return [positive, negative]
where -- etc.
The type of mapM_
is:
GHCi> :t mapM_
mapM_ :: (Foldable t, Monad m) => (a -> m b) -> t a -> m ()
positive
and negative
are functions which already have appropriate types to be passed to mapM
, and so if you want pos_neg_case
to give them back as a list you don't need to do anything more than wrapping in a list. return
is not a keyword; it is just a function that injects a value into a monadic context. If you don't need to do any such injection, you don't need return
.
P.S.: Quoting your answer:
But I had to guess the Parser type, hole was giving me very complex thingy
Text.Parsec.Prim.ParsecT s () Data.Functor.Identity.Identity a -> [s -> IO ()]
.
This is an example of a quite common pattern. ParsecT
is a type constructor with quite a few type variables, and Parser
is a type synonym for a common set of choices of some of these variables, which allows for tidier type signatures which do not mention them explicitly. If you look for it in the documentation (the index helps a lot in such cases) or use :info
in GHCi, you will find that Parser
just means...
type Parser = Parsec String ()
... and Parsec
, in turn is...
type Parsec s u = ParsecT s u Identity
... so that expanding the Parser
synonym gives ParsecT String () Identity
, which is what GHC told you when you introduced the type hole.