Example:
<source lang="haskell">
-- foo
let x = foo
</source>
Hobby-hacking Eric
<source lang="haskell">
-- foo
let x = foo
</source>
<source lang="haskell">
-- foo
let x = foo
</source>
How do I make my tests easy to run? Do I have to write my own RunTests module? Should I just use something like quickcheck-script?And one of the replies I got:
I'm sure people are writing tests, but we all hack up harnesses in our own idiosyncratic ways.... -- blackdogMaybe we can do better. Instead of everybody hacking up their own harness, how about having one test harness that everybody wants to use? We may even have a candidate for such a harness. Reinier Lamers has recently released a "testrunner" package which supports some rather nice features:
How do I make my tests easy to run? Do I have to write my own RunTests module? Should I just use something like quickcheck-script?And one of the replies I got:
I'm sure people are writing tests, but we all hack up harnesses in our own idiosyncratic ways.... -- blackdogMaybe we can do better. Instead of everybody hacking up their own harness, how about having one test harness that everybody wants to use? We may even have a candidate for such a harness. Reinier Lamers has recently released a "testrunner" package which supports some rather nice features:
inkscape-layers myfile.svg base > /tmp/foo.svg && inkscape --export-pdf=/tmp/foo.pdf", I get just the base layer which isn't very interesting:
inkscape-layers myfile.svg base zero (and convert the resulting SVG into a PDF as above), I get the zeroth layer:
inkscape-layers myfile.svg base one
inkscape-layers myfile.svg base two
import Data.Maybe (fromMaybe)
import System.Environment (getArgs, getProgName)
import System.IO (hPutStrLn, stdout, stderr)
import Text.XML.Light
main =
do args <- getArgs
pname <- getProgName
case args of
(f:ls) -> go f ls
_ -> hPutStrLn stderr $ unwords [ "Usage:", pname, "filename", "layer1", "[layer2 [.. layer N]]" ]
go f ls =
do d <- goodXML =<< parseXMLDoc `fmap` readFile f
let o = stdout -- we may want to make this more flexible later
hPutStrLn o . showTopElement . wrapTop walk $ d
where
goodXML = maybe (fail "bad XML") return
--
walk x@(Elem el) =
let lbl = fromMaybe "" (findAttr qLABEL el)
x2 = Elem $ el { elContent = map walk (elContent el) }
in case () of _ | not (isLayer el) -> x2
| lbl `elem` ls -> x2
| otherwise -> Text blank_cdata
walk x = x
isLayer el = elName el == qSVG "g" && findAttr qGROUP_MODE el == Just "layer"
qLABEL = qInkscape "label"
qGROUP_MODE = qInkscape "groupmode"
qSVG l = QName l (Just nsSVG) Nothing
nsSVG = "http://www.w3.org/2000/svg"
qInkscape l = QName l (Just nsINKSCAPE) Nothing
nsINKSCAPE="http://www.inkscape.org/namespaces/inkscape"
wrapTop f e =
case f (Elem e) of
(Elem e) -> e
_ -> error "programmer error: top content is not an element"
inkscape-layers myfile.svg base > /tmp/foo.svg && inkscape --export-pdf=/tmp/foo.pdf", I get just the base layer which isn't very interesting:
inkscape-layers myfile.svg base zero (and convert the resulting SVG into a PDF as above), I get the zeroth layer:
inkscape-layers myfile.svg base one
inkscape-layers myfile.svg base two
import Data.Maybe (fromMaybe)
import System.Environment (getArgs, getProgName)
import System.IO (hPutStrLn, stdout, stderr)
import Text.XML.Light
main =
do args <- getArgs
pname <- getProgName
case args of
(f:ls) -> go f ls
_ -> hPutStrLn stderr $ unwords [ "Usage:", pname, "filename", "layer1", "[layer2 [.. layer N]]" ]
go f ls =
do d <- goodXML =<< parseXMLDoc `fmap` readFile f
let o = stdout -- we may want to make this more flexible later
hPutStrLn o . showTopElement . wrapTop walk $ d
where
goodXML = maybe (fail "bad XML") return
--
walk x@(Elem el) =
let lbl = fromMaybe "" (findAttr qLABEL el)
x2 = Elem $ el { elContent = map walk (elContent el) }
in case () of _ | not (isLayer el) -> x2
| lbl `elem` ls -> x2
| otherwise -> Text blank_cdata
walk x = x
isLayer el = elName el == qSVG "g" && findAttr qGROUP_MODE el == Just "layer"
qLABEL = qInkscape "label"
qGROUP_MODE = qInkscape "groupmode"
qSVG l = QName l (Just nsSVG) Nothing
nsSVG = "http://www.w3.org/2000/svg"
qInkscape l = QName l (Just nsINKSCAPE) Nothing
nsINKSCAPE="http://www.inkscape.org/namespaces/inkscape"
wrapTop f e =
case f (Elem e) of
(Elem e) -> e
_ -> error "programmer error: top content is not an element"
(>>=) :: m a -> (a -> m b) -> m bjoin :: m (m x) -> m xjoin mmx =Those last two lines are redundant:
do mx <- mmx
x <- mx
return x
join mmx =
do mx <- mmx
mx
join mmx = mmx >>= (\mx -> mx)That's just
id:join mmx = mmx >>= id
(>>=) require something of type a -> m b? And isn't id giving me m x -> m x? I stared at that for a while, almost panicking. What did I do wrong? And then it clicked. Of course, the a in a -> m b could stand in for any type, including m x. Just because it doesn't have a little m in it, doesn't mean that it's constrained not to have one. a and b doesn't mean we actually have to have two different types. They can, but don't need to. And that, is my "failure to unify", inventing completely illusory constraints and not seeing through them.join is just (>>= id). It took a little struggle, but it was well worth it!m (m a) when thinking of the types instead of what I reported here, m (m x). The reason I reported the later is because I didn't want to confuse the discussion with another stumbling block I have, which is a "failure to rename", i.e. forgetting that two things called a in different contexts are actually two separate things. It's like speaking a foreign language. Just because you are aware that you have to do something, doesn't mean you will always do it automatically. Anyway, the "failure to rename" may very likely have conspired with the "failure to unify" in making me confused for a while)
(>>=) :: m a -> (a -> m b) -> m bjoin :: m (m x) -> m xjoin mmx =Those last two lines are redundant:
do mx <- mmx
x <- mx
return x
join mmx =
do mx <- mmx
mx
join mmx = mmx >>= (\mx -> mx)That's just
id:join mmx = mmx >>= id
(>>=) require something of type a -> m b? And isn't id giving me m x -> m x? I stared at that for a while, almost panicking. What did I do wrong? And then it clicked. Of course, the a in a -> m b could stand in for any type, including m x. Just because it doesn't have a little m in it, doesn't mean that it's constrained not to have one. a and b doesn't mean we actually have to have two different types. They can, but don't need to. And that, is my "failure to unify", inventing completely illusory constraints and not seeing through them.join is just (>>= id). It took a little struggle, but it was well worth it!m (m a) when thinking of the types instead of what I reported here, m (m x). The reason I reported the later is because I didn't want to confuse the discussion with another stumbling block I have, which is a "failure to rename", i.e. forgetting that two things called a in different contexts are actually two separate things. It's like speaking a foreign language. Just because you are aware that you have to do something, doesn't mean you will always do it automatically. Anyway, the "failure to rename" may very likely have conspired with the "failure to unify" in making me confused for a while)


parseXML and in the other bit, I had named it xmlParse.where instead of let...in, but also "higher-level" functions first, "detail" functions laterbuckets :: Ord b => (a -> b) -> [a] -> [ (b,[a]) ]Do you have an odds-and-ends.hs file on your computer?
buckets f = map (\xs -> (f (head xs), xs))
. groupBy ((==) `on` f)
. sortBy (compare `on` f)
parseXML and in the other bit, I had named it xmlParse.where instead of let...in, but also "higher-level" functions first, "detail" functions laterbuckets :: Ord b => (a -> b) -> [a] -> [ (b,[a]) ]Do you have an odds-and-ends.hs file on your computer?
buckets f = map (\xs -> (f (head xs), xs))
. groupBy ((==) `on` f)
. sortBy (compare `on` f)