I use ocamldebug when the bug location is hidden deep inside a
large set of modules, but for simpler cases I prefer to debug by hand.
Say I have a snippet like this
let rec iterator_for_foobar x=
if test1 x
then modify1 x
else
if test2 x
then iterator_for_foobar(modify2 x)
else
if test3 x
then modify3 x
else
if test4 x
then iterator_for_foobar(modify4 x)
else modify5 x ;;
let foobar x = iterator_for_foobar(modify6 x) ;;
If I need to debug a snippet like the above by hand, I typically refactor
it a little bit to make it more easily debugged ; I replace the iterator:'a->'b
function above with a push_once:'a->'a function which then allows me to define
a push_several_times:int->'a->'a, and my debugging will mostly consist of viewing revelant
values of push_several_times.
Now, the only coding patterns I have found so far both have their drawbacks :
Choice 1: adding an adhoc local type
type iterator_state =
Work_still_in_progress of type1
|Computation_finished of type2 ;;
let push_once_for_foobar =
Computation_finished _ ->
failwith("This should never happen")
|Work_still_in_progress x->
if test1 x
then Computation_finished(modify1 x)
else
if test2 x
then Work_still_in_progress(modify2 x)
else
if test3 x
then Computation_finished(modify3 x)
else
if test4 x
then Work_still_in_progress(modify4 x)
else Computation_finished(modify5 x) ;;
let iterator_for_foobar x =
match x with
Computation_finished y -> y
|Work_still_in_progress _->
iterator_for_foobar(push_once_for_foobar x) ;;
let foobar x = iterator_for_foobar(Work_still_in_progress(modify6 x)) ;;
Choice 2: using an option in a pair
let push_once_for_foobar (opt_answer,x)=
if test1 x
then (Some(modify1 x),x)
else
if test2 x
then (None,modify2 x)
else
if test3 x
then (Some(modify3 x),x)
else
if test4 x
then (None,modify4 x)
else (Some(modify5 x),x) ;;
let iterator_for_foobar pair =
let (opt_answer,x) = pair in
match opt_answer with
Some answer -> answer
|None->iterator_for_foobar(push_once_for_foobar pair) ;;
let foobar x = iterator_for_foobar(None,modify6 x) ;;
Both choices 1 and 2 have the drawback that the refactoring is nontrivial
and cannot easily be automated (this is the major issue for me).
Choice 1 has the anti-pattern of a variant case which will never be encountered during the computation flow.
Choice 2 has the anti-pattern of forcing us to add a dummy value which will
never be used and does not “represent” anything.
Are there other patterns improving on both 1 and 2 (perhaps with more sophisticated
types such as GADTs ?)