One of my occasional claims for why I sick with ML is that you can actually debug SML programs (c.f., Haskell, where being laziness makes debugging "interesting" -- great if you want a research project).
But in practice, debugging in SML/NJ can actually be a pain. If you get an exception from one of the library functions, you may end up with an unhelpful error message like this one:
uncaught exception Domain [domain error] raised at: Basis/Implementation/real64.sml:88.32-88.46
Without any sort of backtrace, you get no clue about where/how the exception was raised.
But it turns out that there is a feature in SML/NJ that lets you get a backtrace. It's just that it's barely documented at all!
It turns out that if you type:
CM.make "$smlnj-tdp/back-trace.cm"; SMLofNJ.Internals.TDP.mode := true;
when you first start SML, and then compile your code, when you get an exception, you'll get a backtrace.
Now, you'll see something more like:
CALL art.sml:52.7-52.55: Art.toIntensity (from: art.sml:89.38-89.57: Art.emitGray.iz) CALL art.sml:79.27-93.33: Art.emitGray (from: art.sml:13.26-13.29: Art.for) GOTO art.sml:10.7-13.45: Art.for (from: art.sml:78.22-93.34: Art.emitGray) CALL art.sml:77.19-93.34: Art.emitGray (from: art.sml:13.26-13.29: Art.for) CALL art.sml:10.7-13.45: Art.for (from: art.sml:75.14-93.35: Art.emitGray) CALL art.sml:64.7-98.7: Art.emitGray (from: ???) CALL art.sml:249.7-307.9: Art.doMix (from: ???) uncaught exception Domain [domain error] raised at: Basis/Implementation/real64.sml:88.32-88.46
Cool. You've got to wonder though, why people would write a cool and useful feature like this and not clearly tell people about it.