Friday, November 07, 2014

Do not confuse data logic and execution logic

I mentioned in my last post that I was looking for a bug in my autoindentation code.

Sadly, strong typing had given me a hint of my problem: I was returning a boolean that I had no use for, but I chose to ignore this by adding an operator that "swallowed" this return value. Yet the source of that boolean return value was my bug.  It just took me extra bit of time to find it.

My parser monad is like a Maybe monad: when a monadic expression fails,  the current expression branch stops and returns the failure up the call stack. If the failure happens to be within a higher order control structure, like an "or" statement or a "star" or "plus" repetition (for pattern matching), then another expression branch may be pursued which may succeed.

On the other hand, a monadic  expression may return a value. For example, a number may be parsed, and the number may be returned by the monadic expression.

It just so happened that my test to check if I was starting or not a block (for the autoindentation detection) was conceptually returning a boolean. Yet in fact it was not, it was either succeeding, or not, in its "higher order monadic logic". The correct code is something like: "if cond then return () else fail withErrorTagX" , while the false code reads "return cond" . When the second code fails, it returns a the failure (as a boolean) but it does not cause a change of execution behavior.

My autoindent code had three monadic functions: beginBlock, endBlock,  checkNotBeginOrEnd.
The two first functions had the correct logic,  the "check..." didn't. This last function was doing what its name was implying: it was checking, with pedantic code in the form of "if beginOfBlock then return false else if endOfBlock then return false else return true". But it should have said "if .. then fail ..."

I had two goals with this post. The first was to make clear the "dual level" of thinking that goes on when working with monadic constructions that have their own execution semantics. My second goal was to remark that execution logic is not the same as good old boolean logic. In my monadic operators, I have boolean operators, like "or", "and", "andNot". I come to realize that this denomination is not good because these are really not boolean operators and should not be confused with them.

Monday, November 03, 2014

Breakpoints in monadic code

I was trying to get my autoindenting parser working again in my old monadic F# code under Xamarin. To note that breakpoints are still an issue with higher order code. This post presents a simple way I get around the problem.

The problem is that higher order code, is first run to be assembled as higher order constructions, and then only later run "to do something". For example, monads are first bound, and then "run". The problem is that a breakpoint sets a stop during this construction phase, not during the execution phase. That is reasonable as the construction is what the top level function does, but it is of little use to the developer who wants to break the execution of the code and not its construction.

My parser monad is very combinatorial. I have operators for the classical parsing constructions like "and" and "or", but also a bunch of helper operators that are multiple variations of the bind operator, (e.g. combine two monads, return value of first), as well as error management helpers, and as well as monadic y-combinators to bootstrap recursive structures. When I use these, I rarely need to define functions on the fly, and therefore I end up with very few or even no anchor points for breakpoints.

To help with setting breakpoints, I have two functions both with the signature m<'a>->('a->'a)->m<'a> (give it a monad and a function and it  returns a monad). I define these as operators %>> and >>%. They are a form of restricted map function, the first calls the function before the execution of the monad, the second calls the function  after the execution of the monad. To use these, I define special breakpoint functions, for example "let breakHere x = x , on which I set breakpoints and which I slip in to my code, for example ... (m %>> breakHere) ... will break before the monad m.

This method needs a rebuild to set a new breakpoint. That does restrict its usage, but it is still very helpful.