[m-dev.] for review: quickcheck user guide

Mark Anthony BROWN dougl at cs.mu.OZ.AU
Fri Feb 16 17:55:29 AEDT 2001


Xiao Chun Simon MEI writes:
> Index: quickcheck/tutes/T7.html
> ===================================================================
> RCS file: T7.html
> diff -N T7.html
> --- /dev/null	Wed Nov 15 09:24:47 2000
> +++ T7.html	Mon Feb  5 16:01:53 2001
> @@ -0,0 +1,267 @@
> +<html>
> +<head> 
> +        <TITLE> QuickCheck </TITLE>
> +</head>
> +Files : 
> +<a href="use71.m">use71.m</a><BR>
> +<a href="index.html">Back to main</a>
> +<H1> QuickCheck Tutorial 7 </H1>
> +<h3> Generators - General Frequency</h3>
> +<pre>
> +
> +General Frequency follows the ideas in Specific Frequency. You must understand
> +the format of Specific Frequency in order to write General Frequency. However 
> +the amount of work in GF is usually less then in SP. In GF, the user should 
> +specify one level down in branches, where a practicl SP may be 3+ level down. 
> +
> +Back in Tutorial 2, an invariant function was written to test the law 

It's actually in tutorial 3, or tutorial 1.3, depending on how you count
them.  At the end of all the reviews, you will need to go through the
entire set of tutes and carefully ensure all the cross-references are
right.

> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +	 reverse (reverse xs) = xs

I think it would be better if this law was written above the table.  It
would look better if the table just contained Mercury code, IMHO.

> +
> +:- func testing4(list(float)) = property.
> +testing4(Xs) = 
> +         list_length(Xs) `>>>` (testrev(testrev(Xs)) `===` Xs).
> +
> +Test Description : testing4
> +Number of test cases that succeeded : 100
> +Number of trivial tests : 0
> +Number of tests cases which failed the pre-condition : 0
> +Distributions of selected argument(s) : 
> +1     8
> +1     4
> +1     6
> +2     5
> +8     3
> +16     2
> +18     1
> +53     0
> +
> +The display of testing4 shows that      53 cases of lenggh == 0
> +                                        18 cases of length == 1
> +                                        16 cases of length == 2
> +                                        ...etc...       
> +</pre></tr></table>
> +The length of of the list is heavily biased towards the short end. In the limit with
> +even distribution along the branchse, the list will have :

s/branchse/branches/

> +50%	length == 0
> +25%	length == 1
> +12.5%	length == 2
> +6.25%	length == 3
> +3.125%	length == 4
> +...etc... halfing the probability in eash step, this is due to the fact that 
> +<h4>
> +	:- type list(T) ---> [] ; [T | list(T)].
> +</h4>
> +Transform the type definition to something more readable as a disciminated union:
> +<h4>
> +	:- type list(T) ---> [] ; .(T, list(T)).
> +</h4>

I think the first form is more readable.  If you want to make it clearer
as a discriminated union, then how about

	:- type list(T)
		--->	[]
		;	[T | list(T)].

In any case, you haven't completed that explanation -- I think the point
you are trying to make is that at any given length accumulated so far,
there is a 50-50 chance of either stopping there (if the first branch
is chosen) or going on to a longer list (if the second branch is chosen).

> +
> +The 5th argument of qcheck/7 takes General Frequency, it's of type 
> +	list({type_desc, list(frequency)})
> +each element of the list contains the General Frequency for a type.
> +The list length can be increased if the 2nd branch if favoured over the 1st branch

s/branch/branch./

> +<h4>
> +From Tute6 :
> +frequency defines the relative chance of a branch being selected, plus information of that

s/information of/information about/

> +branch's sub-branches.
> +list(frequency) contains distribution information about 1 discrimated union, ie: the list 

s/discrimated/discriminated/

and also below.

> +should contain frequencies for all possible branches.
> +list(list(frequency)) contains distribution information about a list of discrimated unions.
> +</h4>
> +A list is a discriminated union, to describe it, the correct format will be list(frequency)

"A list is a discriminated union" should be a sentence on its own.

> +which matches the 2nd term of list({type_desc, list(frequency)})
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +list(frequency) for list(T), list has two branches, so list of 2 element

How about "The list(frequency) for list(T) should have two elements,
since the definition of list/1 has two branches."

> +	Output = [ frequency_a, frequency_b ]
> +
> +	frequency_a = { 10, ...something_a... } 
> +	frequency_b = { 90, ...something_b... }
> +That is 10% take 1st branch, 90% take the 2nd branch.
> +
> +The constrctor []/0 takes no argument, thus something_a is [], ie:
> +frequency_a = { 10, [] } 
> +
> +The 2nd branch constructor .(T, list(T)) takes 2 arguments. In specific frequency that would 
> +mean a list of two elements (or choose defalut []) :
> +	something_b = list(list(frequency)) = [ A, B ]
> +In General Frequency the user can specify down more than 1 level, however it is not required 
> +in this case. Define something_b = { 90, [] }.
> +
> +Put it all together: 
> +		list(frequency)
> +	=	[ frequency_a, frequency_b ]
> +	=	[ { 10, [] } , { 90, [] }  ]
> +
> +Then
> +		{ type_desc, list(frequency)}

Put a space before the closing brace.

> +	=	{ type_of([0.0]), [ { 10, [] } , { 90, [] }  ] }
> +
> +Now for type list(float), there is 10% chance of seleting 1st branch, and 90% chance of selecting

s/seleting/selecting/

> +2nd branch at EVERY level.
> +</pre></tr></table>
> +
> +The complete code (use71.m) :
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +:- module use71.
> +
> +:- interface.
> +
> +:- use_module io.
> +
> +:- pred main(io__state, io__state).
> +:- mode main(di, uo) is det.
> +
> +%---------------------------------------------------------------------------%
> +
> +:- implementation.
> +
> +:- import_module int, float, list, std_util.
> +:- import_module qcheck, testrev.
> +
> +%---------------------------------------------------------------------------%
> +
> +main -->
> +	{ freq_list(F) },	
> +     	qcheck(qcheck__f(testing4), "testing4", 1000, [], [F]).
> +
> +:- pred freq_list({type_desc, list(frequency)}).
> +:- mode freq_list(out) is det.
> +freq_list(F) :-
> +	F = { type_of([0.0]), [ { 10, [] } , { 90, [] }  ] }.
> +
> +:- func testing4(list(float)) = property.
> +testing4(Xs) = 
> +	list_length(Xs) `>>>` (testrev(testrev(Xs)) `===` Xs).
> +</pre></tr></table>
> +A sample output shows the lists are much longer

You should end that with a full-stop or colon.

> +<h4>
> +Test Description : testing4
> +Number of test cases that succeeded : 1000
> +Number of trivial tests : 0
> +Number of tests cases which failed the pre-condition : 0
> +Distributions of selected argument(s) : 
> +1     39
> +1     56
> +2     40
> +2     36
> +2     38
> +2     46
> +2     61
> +2     28
> +2     50
> +2     33
> +2     53
> +2     41
> +3     43
> +3     32
> +4     34
> +4     37
> +4     31
> +5     26
> +5     30
> +7     21
> +7     35
> +8     25
> +8     29
> +9     22
> +9     23
> +11     19
> +14     24
> +14     17
> +15     27
> +16     18
> +16     20
> +18     14
> +19     16
> +23     13
> +25     15
> +30     11
> +32     12
> +34     9
> +42     10
> +43     8
> +49     7
> +49     6
> +62     4
> +63     3
> +69     5
> +72     2
> +91     1
> +95     0
> +</h4>
> +
> +Summary on default frequency, specific frequency, general frequency

Ditto.

> +
> +1	Before Quickcheck generate a term, it checks which types the term is

s/generate/generates/

and also below.

> +	suppose to be. If it's not a discriminated union, all the frequency 

That would be better written as:

	Before Quickcheck generates a term, it considers the term's
	type.

> +	input is ignored and lost forever.
> +2	If it is a discriminated union, then Quickcheck generate the term 
> +	according to the specifici frequency. In cases where the specific 

s/specifici/specific/

> +	frequency is [], then Quickcheck will search the general frequency 
> +	list to find the matching type. (between type_of(term) and what's in	
> +	the list)

I don't understand that comment in brackets.

> +3	If no matching type found, then generate the term (at this level) evenly

s/type found/type is found/

> +	eg:	:- type coin 
> +			--->	small(face)
> +			;	large(color).
> +		:- type face
> +			--->	head
> +			;	tail.
> +	The chance between small/large is even, but that doesn't mean chance 
                                                                    ^^^
s/chance/the chance/

> +	between face:head/tail is even.
> +4	If matching type is found, then Quickcheck copies that frequency information,
> +	and treat that as the specific frequency. 

s/treat/treats/

> +
> +In the list(float) example: 
> +<h4>
> +	qcheck(qcheck__f(testing4), "testing4", 1000, [], [F])
> +	F = { type_of([0.0]), [ { 10, [] } , { 90, [] }  ] }
> +</h4>   	
> +Quickquick will first find [] as specific frequency (since [] is passed to qcheck), 

s/Quickquick/Quickcheck/

> +then it will find general frequency list contains information on how to generate 

I would say:

	so it will look in the general frequency list for information on how...

> +list(float). That information will be extracted. The function whish generates 
s/whish/which/

> +discriminated union will behave as if it was called with specific frequency equal to 
> +that of the information extracted. The information in GF is only 1 level deep, it can 
> +be use only once, after that the specific frequency will be [] again. So if the 

s/use/used/

> +2nd branch is choosen and another list(float) is needed, then quickcheck will find [] 

s/choosen/chosen/

> +as specific frequency, then it will find general frequency list contains information on 
> +how to generate list(float). That information will be copied over...etc...
> +That is:
> +	0	enter generator
> +	1	SF = [], 
> +	2	search GF, found, [ { 10, [] } , { 90, [] }  ]
> +	3	restart the generator with SF =  [ { 10, [] } , { 90, [] }  ]
> +	4	SF = [ { 10, [] } , { 90, [] }  ], do not search GF
> +	5	suppose 2nd branch is chosen, ie { 90, [] }
> +		the sub-branch has SF = [] 
> +	6	generate the subbranch with SF = []

s/subbranch/sub-branch/

> +
> +	7a	enter generator (for the sub-branch)
> +	7b	SF = [], 

I think it would be better to write

		SF = [], go back to step 1

and leave off the rest of the steps.

> +	7c	search GF, found, [ { 10, [] } , { 90, [] }  ]
> +	7d	restart the generator with SF =  [ { 10, [] } , { 90, [] }  ]
> +	7e	SF = [ { 10, [] } , { 90, [] }  ], do not search GF
> +	7f	suppose 2nd branch is chosen, ie { 90, [] }
> +		the sub-branch has SF = [] 
> +	7g	generate the subbranch with SF = []
> +	....
> +
> +The actual code does not restart the generator.
> +
> +
> +
> +
> +
> +
> +
> +
> +
> + 
> +
> +
> +
> +
> Index: quickcheck/tutes/T8.html
> ===================================================================
> RCS file: T8.html
> diff -N T8.html
> --- /dev/null	Wed Nov 15 09:24:47 2000
> +++ T8.html	Mon Feb  5 16:01:53 2001
> @@ -0,0 +1,145 @@
> +<html>
> +<head> 
> +        <TITLE> QuickCheck </TITLE>
> +</head>
> +Files : 
> +<a href="use81.m">use81.m</a><BR>
> +<a href="index.html">Back to main</a>
> +<H1> QuickCheck Tutorial 8 </H1>
> +<h3> Generators - Random Functions</h3>
> +<pre>
> +
> +Random function is generated using curry, currently only generate functions that 

s/Random function is/Random functions can be/

What do you mean by "using curry"?  If you are referring to how qcheck
is implemented then you can omit this comment, since users don't really
need to know about implementation details.

> +have 1 ~ 3 inputs, and 1 output. The generated function can run on any type of

You should check that these figures are consistent with the current
implementation.

> +inputs, but the output must be of type0-7 from tute 5.  

Rather than saying "type0-7 from tute 5", it would be better to say that
the output must be a type for which we can generate arbitrary values.

> +Also each input argument has 5% chance of being independent of the output.

I don't understand what you mean by "independent" here.

> +(currently the invariant function can only have forward mode functions as its
> +input arguments)

The comment in brackets is not a proper sentence.

> +
> +An example (use81.m)
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +:- module use81.
> +
> +:- interface.
> +
> +:- use_module io.
> +
> +:- pred main(io__state, io__state).
> +:- mode main(di, uo) is det.
> +
> +%---------------------------------------------------------------------------%
> +
> +:- implementation.
> +
> +:- import_module int, char, std_util, list.
> +:- import_module qcheck, rnd.
> +
> +:- type strange(T1, T2)
> +        --->    wierd(T1, T2, T1).
> +
> +main -->
> +	qcheck(qcheck__f(prop1), "prop1", 1000, [], []),
> +	qcheck(qcheck__f(prop2), "prop2", 1000, [], []),
> +	qcheck(qcheck__f(prop3), "prop3", 1000, [], []),
> +	qcheck(qcheck__f(prop4), "prop4", 1000, [], []).
> +
> +:- func prop1(func(strange(int, char), int) = list(int)) = property.
> +:- mode prop1(in(func(in,in)=out is det)) = out is det.

It would be better if that were spaced out, as per the usual coding
style.

> +prop1(FFF) = 
> +	FFF(wierd(1, '0', 2), 999) `===` FFF(wierd(1, '0', 2), 999).
> +
> +:- func prop2(func(strange(int, char), int) = list(int)) = property.

Shouldn't this also have a mode declaration?  Same with the others below.

> +prop2(FFF) = 
> +	(if	FFF(wierd(1, '0', 2), 999) = FFF(wierd(1, '0', 2), 888)
> +	 then
> +		[ info(univ("equal")) | [yes] ] 
> +	 else
> +		[ info(univ("not equal")) | [yes] ] 
> +	).
> +
> +:- func prop3(func(int) = int) = property.
> +prop3(FFF) = 
> +     	(if	FFF(1) = FFF(0)
> +         then
> +                [ info(univ("equal")) | [yes] ]
> +         else
> +                [ info(univ("not equal")) | [yes] ]
> +        ).
> +        
> +:- func prop4(func(int, int) = int) = property.
> +prop4(FFF) = 
> +     	(if	FFF(101, 10) = FFF(11, 15)
> +         then
> +                [ info(univ("equal")) | [yes] ]
> +         else
> +                [ info(univ("not equal")) | [yes] ]
> +	).	
> +</pre></tr></table>
> +
> +A Sample OutPut:

s/Sample OutPut/sample output/

> +<h4>
> +Test Description : prop1
> +Number of test cases that succeeded : 1000
> +Number of trivial tests : 0
> +Number of tests cases which failed the pre-condition : 0
> +Distributions of selected argument(s) : 
> +
> +Test Description : prop2
> +Number of test cases that succeeded : 1000
> +Number of trivial tests : 0
> +Number of tests cases which failed the pre-condition : 0
> +Distributions of selected argument(s) : 
> +256     "equal"
> +744     "not equal"
> +
> +Test Description : prop3
> +Number of test cases that succeeded : 1000
> +Number of trivial tests : 0
> +Number of tests cases which failed the pre-condition : 0
> +Distributions of selected argument(s) : 
> +60     "equal"
> +940     "not equal"
> +
> +Test Description : prop4
> +Number of test cases that succeeded : 1000
> +Number of trivial tests : 0
> +Number of tests cases which failed the pre-condition : 0
> +Distributions of selected argument(s) : 
> +1     "equal"
> +999     "not equal"
> +</h4>
> +
> +prop1/1 just shows that given the same input, the output of of the

s/of of/of/

> +sample function is the same.
> +
> +prop3/1 shows that give different input, the random function will 

s/give/given/

> +usually give different output (940/1000 cases). For the rest 60/1000 
> +cases, either the output is independent of input (most likely). Or 
> +it just so happens the output is the same for FFF(0) and FFF(1).
> +(Noet 60/1000 is roughly 5%)
> +
> +prop2/1 shows 256/1000 cases shows that the random function returned the 
> +same output for different inputs. The real differenct between prop3/1 and
> +prop2/1 is that the output of prop2/1 is list(int), which means the chance
> +of generating [] is high (50% chance), and that's where 256/1000 cases come 
> +from.
> +
> +prop4/1 's random function takes two inputs, the chance of both inputs being
> +independent of output is less compare to a random function which only takes
> +1 input. eg FFF(in)     = out, out independent of in is 5% chance
> +	    FFF(in, in) = out, out independent of in is 5% * 5% chance
> +
> + 
> +
> +
> +
> +
> +
> +
> +
> + 
> +
> +
> +
> +
> Index: quickcheck/tutes/T9.html
> ===================================================================
> RCS file: T9.html
> diff -N T9.html
> --- /dev/null	Wed Nov 15 09:24:47 2000
> +++ T9.html	Mon Feb  5 16:01:53 2001
> @@ -0,0 +1,167 @@
> +<html>
> +<head> 
> +        <TITLE> QuickCheck </TITLE>
> +</head>
> +Files : 
> +<a href="use91.m">use91.m</a><BR>
> +<a href="index.html">Back to main</a>
> +<H1> QuickCheck Tutorial 9 </H1>
> +<h3> User Defined generators</h3>
> +<pre>
> +
> +From Tute 6:
> +The invariant function is of the form 
> +        :- func XXXXXXXX(T,  T1, T2 ...) = property
> +        :- mode XXXXXXXX(in, in, in ...) = out.
> +Quickcheck generates random value for each input argument at run time.
> +Currently quickcheck have default generators for the following types :
> +        0       equivalent type
> +        1       int
> +        2       char
> +        3       float
> +        4       string
> +        5       discriminated union
> +        6       some functions
> +also type 7 which is any thing the user has written a generator for.

I don't see a need to reproduce this information here.  This is a double
maintenance problem: if somebody updates the set of types that have
default generators, they might fix the first occurrence of this list (in
the earlier tute) but miss this one here.  If you want the users to have
easy access to this information, you should provide a link to it rather
than copy it.

> +
> +Default generator will be called only if a user defined generator does not 

s/Default/The default/

> +exsit. User definded generator must conform to the following format:

s/exist/
s/User definded/The user defined/

> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +:- func gen_f(type_desc, list(frequency), list({type_desc, list(frequency)}),
> +              list(user_gen_type), rnd, rnd) = univ.
> +:- mode gen_f(in, in, in, list_skel_in(user_gen_inst), in, out) = out is det.
> +
> +:- type user_gen_type 
> +	--->	{ type_desc, 
> +		  func(type_desc, list(frequency), 
> +		       list({type_desc, list(frequency)}), 
> +		       list(user_gen_type), rnd, rnd) = univ
> +		}.
> +
> +	%	inst declaration for each user-defined generator	
> +:- inst user_gen_inst
> +	=	bound({ ground, 
> +		        func(in, in, in, 
> +			     list_skel_in(user_gen_inst), in, out) = out is det
> +		      }).
> +</pre></tr></table>
> +The last two arguments of type rnd, is the random supply, in & out.

s/is/are/

> +(Ignore the rest of arguments) use91.m shows a user defined generater for type :

s/generater/generator/

> +<h4>
> +	func fff(int) = int	
> +	mode fff(in) = out
> +				
> +</h4>
> +<P>
> +<table border=0 width=100% bgcolor=#eeeee0><tr><td><pre>
> +:- module use91.
> +
> +:- interface.
> +
> +:- use_module io.
> +
> +:- pred main(io__state, io__state).
> +:- mode main(di, uo) is det.
> +
> +%---------------------------------------------------------------------------%
> +
> +:- implementation.
> +
> +:- import_module int, list, std_util.
> +:- import_module qcheck, rnd.
> +
> +main -->
> +        qcheck(qcheck__f(prop1), "user function", 100, [], [], 
> +	       [{type_of(some_function), gen_f}]),
> +        qcheck(qcheck__f(prop1), "no user function", 100, [], [], []). 
> +
> +:- func gen_f(type_desc, list(frequency), list({type_desc, list(frequency)}),
> +                list(user_gen_type), rnd, rnd) = univ.
> +:- mode gen_f(in, in, in, list_skel_in(user_gen_inst), in, out) = out is det.
> +gen_f(_, _, _, _, RS, RS) = Univ :-
> +	Univ = univ(some_function).
> +
> +:- func some_function(int) = int.
> +:- mode some_function(in) = out is det.
> +some_function(X) = Y :-
> +	Y = 2 * X.
> + 
> +:- func prop1(func(int)=int) = property.
> +:- mode prop1(in(func(in)=out is det)) = out is det.
> +prop1(FFF) = FFF(8) `===` (8 * 2).  
> +
> +Sample output:
> +	Test Description : user function
> +	Number of test cases that succeeded : 100
> +	Number of trivial tests : 0
> +	Number of tests cases which failed the pre-condition : 0
> +	Distributions of selected argument(s) : 
> +
> +	Test description : no user function
> +	Falsifiable : 
> +	'<<predicate>>'
> +</pre></tr></table>
> +
> +The user defined generator is gen_f/6, which ignores the first 4 argurment, and 

s/argurment/arguments/

> +assign random_supply output equals to the input since gen_f doesn't use it.

s/assign/assigns/
s/random_supply/rnd/
s/equals/equal/

> +(all that was trivial, basiclly gen_f/6 ignores all of the inputs) And it returns 
> +some_function/1 in a univ.
> +
> +<h4>
> +:- pred qcheck(T, string, int, list(list(frequency)),
> +               list({type_desc, list(frequency)}), 
> +               list(user_gen_type), io__state, io__state) <= testable(T). 
> +:- mode qcheck(in, in, in, in, in, list_skel_in(user_gen_inst), di, uo) is det.  
> +qcheck(qcheck__f(prop1), "user function", 100, [], [], 
> +	       [{type_of(some_function), gen_f}]), 
> +</h4>
> +qcheck/8 is used in use91.m. The last argument takes in the user-defined 
> +generators. The format is list(user_gen_type), since there is only 1 user 
> +defined generator, the list contains 1 element : {type_of(some_function), gen_f}.
> +	type_of(some_function)	:	describes what type gen_f generates
> +	gen_f			:	is the actual generator
> + 
> +the invariant function is true only if FFF(8) `===` (8 * 2). The 2nd test shows a random
> +function will 'never' do that, but the 1st test with user defined generator will always 
> +generator functions Y = 2 * X

"... will always generate the function Y = 2 * X."

> +<h4>
> +:- func gen_f(type_desc, list(frequency), list({type_desc, list(frequency)}),
> +              list(user_gen_type), rnd, rnd) = univ.
> +:- mode gen_f(in, in, in, list_skel_in(user_gen_inst), in, out) = out is det.
> +</h4>
> +
> +1st input of type	type_desc	is the type_of of the current term the generator 
> +					is suppose to generate. Not much use; can be used 
> +					like an assertion.

What do you mean by an assertion?  If it is not much use, then why is it
there at all?

> +
> +2st input of type	list(frequency) is the specific frequency for this term, meaningless
> +					if the term is not a discriminated union. The user 
> +					can choose to ignore the setting even if the term is
> +					a discriminated union.
> +
> +3rd input of type	list({type_desc, list(frequency)}) 	is a list of general frequency
> +
> +4th input of type	list(user_gen_type)	is a list of user defined generators 
> +				
> +5th input of type	rnd		random_supply input
> +
> +6th input of type	rnd		random_supply output, let output = input if not used.

random_supply is actually a different type to rnd.  So it would be
better to do something like:

s/random_supply/random number supply/

> +
> +Look at rand_union/6 to find out how to wrote code which extract/analysis specific frequency amd
> +general frequency. 
> +Look at gen/7 to find out how to wrote code which extract/analysis user defined generators

s/wrote/write/

"extract/analysis" should be "extracts/analyses"

> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> + 
> +
> +
> Index: quickcheck/tutes/index.html
> ===================================================================
> RCS file: index.html
> diff -N index.html
> --- /dev/null	Wed Nov 15 09:24:47 2000
> +++ index.html	Mon Feb  5 16:01:53 2001
> @@ -0,0 +1,36 @@
> +<html>
> +<head> 
> +	<TITLE> QuickCheck </TITLE>
> +</head>
> +
> +<H1> Quickcheck for Mercury</H1>
> +
> +<H3> HTML Version </H3>
> +
> +<ul>
> +<li> <a href="T1.html"><pre>Tutorial 1.1	Intro & `===`</pre></a>
> +<li> <a href="T2.html"><pre>Tutorial 1.2	Conditional laws & `===>``</pre></a>
> +<li> <a href="T3.html"><pre>Tutorial 1.3	Monitoring Data, to_trivial/3, `>>>`</pre></a>
> +<li> <a href="T4.html"><pre>Tutorial 1.4	Summary - Invariant Function & Property </pre></a>
> +<li> <a href="T5.html"><pre>Tutorial 2.1	Generators basic</pre></a>
> +<li> <a href="T6.html"><pre>Tutorial 2.2	Discriminated union & Specific Frequency</pre></a>
> +<li> <a href="T7.html"><pre>Tutorial 2.3	General Frequency</pre></a>
> +<li> <a href="T8.html"><pre>Tutorial 2.4	Random Functions</pre></a>
> +<li> <a href="T9.html"><pre>Tutorial 3.1	User Defined Generators</pre></a>
> +</ul>

These numbers are different to what appears in the header of each page.

> +
> +<p>
> +----------------
> +<p>
> +
> +<H3> MS Word Version </H3>
> +<ul>
> +<li> <a href="T1.doc">Tutorial 1</a>

I haven't reviewed this.

> +<li> Tutorial 2</a>
> +<li> Tutorial 3</a>
> +</ul>
> +
> +Feedback? Mail <a href="mailto:xcsm at students.cs.mu.oz.au">Simon</a>.
> +
> +</BODY>
> +</html>
> Index: quickcheck/tutes/mymax.m
> ===================================================================
> RCS file: mymax.m
> diff -N mymax.m
> --- /dev/null	Wed Nov 15 09:24:47 2000
> +++ mymax.m	Mon Feb  5 16:01:53 2001

Having the code in *.m files in the directory at the same time as having
a copy in the *.html files is another double maintenance problem.

To be continued ...

Cheers,
Mark.

--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list