Unless I'm missing something, your Eq
implementation of RoseT
is the same as the default derived implementation. So you can just say
data RoseT a = Leaf a | Node a [RoseT a] deriving (Show, Eq)
and forget the instance Eq (RoseT a) where
stuff.
The next question is whether or not this will meet your needs for testing. If you're testing with a floating-point type parameter, e.g., RoseT Double
, then you need to allow for differences due to rounding. In that case what you need is a function that compares two trees and sees if the values are "close enough".
However, I suspect your RoseT
implementation won't depend in any way on the type parameter. In that case, you could just test it with a nice simple type like Char
or Int
, and use ==
for any comparisons you need.
You have two type signatures for appendPath
. I think the second one was supposed to be appendPath'
:
appendPath' :: [a] -> (RoseT a) -> (RoseT (a,[a]))
Now for how to test it. It would be best if you/QuickCheck have some control over the complexity of the trees being tested. This will help you because the simplest trees will be tested first, so you find bugs "early" (i.e., with simpler test cases that are easier to debug). You can do this by implementing a "sized" generator for your class. Here's one way to do it. The higher the value of the "size" parameter, the more levels the tree is likely to have.
type TestRoseT = RoseT Char
sizedArbTestRoseT :: Int -> Gen TestRoseT
sizedArbTestRoseT 0 = do
c <- arbitrary
return $ Leaf c
sizedArbTestRoseT n = do
c <- arbitrary
subtreeCount <- choose (0,n-1)
subtrees <- vectorOf subtreeCount (sizedArbTestRoseT (n-1))
return $ Node c subtrees
instance Arbitrary TestRoseT where
arbitrary = sized sizedArbTestRoseT
prop_appendPath_does_something :: TestRoseT -> Property
prop_appendPath_does_something t = undefined -- stub
We can sample the test data that's generated like so:
λ> sample (sizedArbTestRoseT 2)
Node '\a' [Node '\RS' []]
Node '?' []
Node '\158' []
Node 'o' [Node 'E' []]
Node '\196' []
Node '4' [Node 'G' []]
Node ';' [Node 'f' []]
Node 'A' [Node '\CAN' []]
Node '!' []
Node 'q' [Node '\t' []]
Node '\'' [Node '\212' []]
Edit:
For your Expr
type, we can write a generator like so:
sizedArbExpr :: Int -> Gen Expr
sizedArbExpr 0 = do
s <- arbitrary
return $ Var s
sizedArbExpr n = do
es <- vectorOf 2 (sizedArbExpr (n-1))
elements [AddExpr es, MulExpr es]
instance Arbitrary Expr where
arbitrary = sized sizedArbExpr
We'll need to modify TestRoseT
and its generator so that the complexity of the tree is consistent with the "size" parameter:
type TestRoseT = RoseT Expr
sizedArbTestRoseT :: Int -> Gen TestRoseT
sizedArbTestRoseT 0 = do
c <- sizedArbExpr 0 -- changed this to keep complexity in bounds
return $ Leaf c
sizedArbTestRoseT n = do
c <- sizedArbExpr (n-1) -- changed this to keep complexity in bounds
subtreeCount <- choose (0,n-1)
subtrees <- vectorOf subtreeCount (sizedArbTestRoseT (n-1))
return $ Node c subtrees
Testing these modifications gives something like:
λ> sample (sizedArbTestRoseT 3)
Node (MulExpr [MulExpr [Var "",Var ""],AddExpr [Var "",Var ""]]) [Node (MulExpr [Var "",Var ""]) [Node (Var "") []]]
Node (MulExpr [AddExpr [Var "",Var ""],AddExpr [Var "",Var ""]]) [Node (AddExpr [Var "",Var ""]) []]
Node (MulExpr [AddExpr [Var "\164D",Var "\151\246\FS"],MulExpr [Var ":\149j\EM",Var "h\253\255"]]) [Node (MulExpr [Var "\CAN\a\ACK",Var "\184"]) [Node (Var "t\154]\\") []],Node (MulExpr [Var "\135",Var "\f\DEL\\"]) [Node (Var "\SOH\DEL") []]]
Node (AddExpr [AddExpr [Var "",Var ""],MulExpr [Var "Kj\STXV",Var "D\141<s\187"]]) []
Node (AddExpr [MulExpr [Var "\252",Var "`"],MulExpr [Var "\167`t\196",Var ":\135\NULdr\237\167"]]) []
Node (AddExpr [MulExpr [Var "]\173\&28D\SOCom",Var "^\196\ETB2\216\&2\GS\ENQ\ENQ"],AddExpr [Var "$bB\212\SOH\146\234",Var "\DC3\213\&3\SUB\\}^\246(\200"]]) [Node (MulExpr [Var "l;\133\EM\147#\SUBN\\\t",Var "\235\151U\129m3|"]) [Node (Var "\NULb\133") []],Node (AddExpr [Var "\187\EOT\165S\207\r\"\RS",Var "4"]) []]
Node (MulExpr [MulExpr [Var "%0eK",Var "`N**k\FS6\NAK"],MulExpr [Var "'lUL\NAKRPc\ENQR",Var "j\232+`\FS@n"]]) [Node (AddExpr [Var "H\DC1C%\DC48<\t\ETX.L",Var "\235+\v\STXX,\NAK\SUBQc="]) [Node (Var "f\254oX?w\224\195~/") []]]
Node (AddExpr [AddExpr [Var "P",Var "\148G\STX\DEL*\136(\161\159\&7"],AddExpr [Var "\218\136-",Var "9?\128\159\b\b%3t}\131qe"]]) [Node (MulExpr [Var "\198\249\&4\176\193/}\DC28",Var ")Gl0ym\GS"]) [Node (Var ")\204\226qA\175") []]]
Node (MulExpr [MulExpr [Var "\t\186r.",Var "j\ENQ\183\NUL\NAK\129:rg[\170o\157g\238"],AddExpr [Var "\218F\226\248\156\225\&1",Var "vu\SOH\138+CKW\EM\167\&1n"]]) [Node (MulExpr [Var ",\241\158={o\182\"5\t\STX\ETX\DC2\218\162",Var "\197\&1"]) [Node (Var "u?a};\238") []]]
Node (MulExpr [MulExpr [Var "*",Var "R"],AddExpr [Var "\CAN8C",Var "\232V.\172ILy\162a"]]) []
Node (MulExpr [MulExpr [Var "\SI\240NF\249-\v$",Var "K`\STX\231w{"],MulExpr [Var "\DC1\255\209",Var "/\227\146\236\STX\185T3r\f"]]) [Node (MulExpr [Var "\229,\DLE\NAKwf[7P\160\DEL",Var "\134#\RS\SI0KCg\195\NAK\"\191\&6\243\193\SI"]) [Node (Var "\226\&7b8\f\EOTgF\171\GS}\189c\SUBc\ETX") []]]
By the way, you said "it seems impossible to write generator that takes type parameter as an argument". Actually it is possible to do that, however I don't think that's what you really want here.
Incidentally, it seems a bit unusual that you want to create a tree (RoseT
) where the leaves contain binary trees (Expr
). In other words, you're creating trees of trees. Of course, I don't know your application.
RoseT
definition seems incorrect. Rose-trees shouldn't haveLeaf
pattern in their ADT, as with that definition it is it is ambiguous whether leaf nodes should be written asLeaf 42
, orNode 42 []
. It is ambiguous because those two expressions should be equal, but they are not. – Hypsometer