Monitoring whether a function happens

I'm fairly new to functional programming. I found myself gravitating towards it without knowing what it was (oddly enough, I did the same with object-orientated programming about 10 years ago).

I'm most interested in avoiding destructive updates, but there's something I can't get.

In a test, I want to know whether a particular function has been run. I'm not sure how to go about that.

In the Clean way, the function that calls that function would return a new World (I think) that somehow returns 'yes' when asked whether that function has been run.

In the Haskell way, I would use monads to emulate a mutable variable (I think).

I'm actually using Java, the program is event-driven (Swing). I think perhaps the World-passing metaphor is invalid because I don't control the code that calls my event handlers (and they ultimately return nothing [void]) anyway.

The actual case is that when a particular label on the screen is changed, I want it to flash. I have a little animation routine that changes its background colour appropriately, but in one case, it doesn't happen. So my test should detect this. I can only see an imperative way of solving that - by adding a PropertyChangeListener that modifies an array element or equivalent to determine whether the 'background' property was changed.

Is there another way - a suggested non-imperative way? I don't mind if the suggestion isn't related to Java, or if it ultimately is invalid in Java - I'm really more interested in the concepts at the moment - obviously I can do imperative stuff in Java to make this test fail (and then fix the bug so that it passes).

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Evaluation <-> Result

I'm actually using Java, the program is event-driven (Swing). I think perhaps the World-passing metaphor is invalid because I don't control the code that calls my event handlers (and they ultimately return nothing [void]) anyway.

What you describe here is almost by definition not functional: you have inputs that are not passed as parameters to the function and there are outputs that aren't accounted for in its return value.

World-passing and monads are both examples of converting non-functional operations to functional ones by turning the global "state" into an explicit or implicit parameter and/or return value.

Coincidentally, good test-driven development encourages functional representations for all operations, since this approach makes it easier to match expected outputs to provided inputs.

The simplest way to convert what you describe to a more functional representation would be to convert the "functions" that currently return void so that they return something (say "true" or a new Object). If a function has returned a result, by definition it has been evaluated.

So how does concurrency work?

If I can't modify stuff, how can I share data between threads? Suppose I spark off two threads that each do a lengthy calculation, and somehow get the result back to the main thread. Do I have to implement a way of merging two Worlds?

I'm slowly reading Tackling the awkward squad by Simon Peyton Jones of Microsoft, which should cover this, if I can ever actually understand it, but meanwhile..

I view the void event handler as being quite similar to concurrency in that I can't pass the modified World around using it; perhaps I need a thin imperative layer that keeps track of the current World, because the event 'loop' is out of my control.

Poorly

Swing requires that all modification of UI-visible state occur in a single thread. Passing data into this thread from other threads is done with some obvious closure-passing utilities. Issues with liveness, responsiveness and deadlock are all handled with, um, a whole lot of programmer care and annoyance, mostly.

Oops

I probably shouldn't have mentioned concurrency here, it's just confused the issue. Swing uses a single-threaded model, maybe due to the stuff discussed here, but more likely because some operating systems can only deliver windowing events to one thread.

Swing's single-threaded approach is just as annoying as concurrency though, because you don't get your World back. A void method can do things that create new Worlds, but it can't return them. Saying that, I'm not sure, if I were redesigning Swing, what I could do to improve the situation.

I don't have an actual problem with handling Swing stuff imperatively, I'm fairly experienced at that, I'm just trying to apply the new stuff I've learned to what I've already got, if I practically can.

A Journey of a Thousand Miles

I'm just trying to apply the new stuff I've learned to what I've already got, if I practically can.

Start by reducing your reliance on global state whenever you can.

If you find yourself about to add a static variable, or create a singleton, or store something in a database/file to be loaded by another part of the program, stop for a moment to think whether you couldn't accomplish the same thing with by passing a local, immutable copy of the data in question as a parameter to an object/method or as a return value back to the caller.

With a bit of practice and effort, you will find that you can often make your data flow much clearer and cleaner this way, and you will likely find that what global state you have left ofter is more isolated and better understood.

I Would Walk one Thousand Miles

I'm generally doing that. I only have one static variable in my entire 22kloc program - and that's only used if I invoke Beanshell (a Java interpreter, kind of) from within the program for debugging purposes.

I'm going through my code and making fields final (or making them locals instead of fields, anonymous class fun), typically wherever I find bugs.

It's not easy though - I find the best technique is to copy a class, 'fix' it, deprecate the original, replace the original incrementally wherever it's used, while testing, and then remove the original. Of course the original is retained in source control. It's hard because sometimes I'll end up having to do the same thing with the code that uses it, and sometimes I'll realise that my tests weren't good enough much later. Still, I think it's progress.

Thanks for the replies.