dtonhofer

dtonhofer

Functional Programming in Java, Second Edition: Chapter 10 - Much too hard on exceptions, some thoughts

Just some thoughts on being hard on exceptions in functional languages.

On p. 167 we read:

Functional style code is amazing, concise, less complex, and easy to work with…until we hit exception handling. Exception handling is fundamentally an imperative style of programming idea. Throwing exceptions is incompatible with functional programming.

I beg to differ. There seems to be a strong feeling about this as the affirmation. It may be ugly but it’s not fundamentally a problem

Exception handling is fundamentally an imperative style of programming idea. Exception handling and functional programming are incompatible

is repeated on page 171. But in fact, a functional language - which in principle has no side effect - would have rather clean semantics regarding exceptions, would it not: just dump all the ongoing computation made since the nearest relevant “catch”. There would not even be a need to worry about anything else, like cleanup through finally blocks. As data structures in functional languages are supposed to be immutable, one would not even have to worry about such structures being left in some intermediate, improper state.

Furthermore we read:

You are not allowed to throw a checked exception from the functional pipeline.

But this is a problem of how functional pipelines have been defined in Java - the syntax does not allow it. It is not a fundamental problem. The fact that one is allowed to throw unchecked exceptions as normally (in fact, that cannot be avoided) indicates as much.

Here is some very interesting commentary on this:

We read at the above:

The simple answer to your question is: You can’t [throw checked exceptions from a stream pipeline], at least not directly. And it’s not your fault. Oracle messed it up. They cling on the concept of checked exceptions, but inconsistently forgot to take care of checked exceptions when designing the functional interfaces, streams, lambda etc. That’s all grist to the mill of experts like Robert C. Martin who call checked exceptions a failed experiment.

Going further in the book on p.167:

Don’t throw a runtime exception either. Sure, the compiler does not stop you from doing so, but you’ll abruptly end the functional pipeline processing and the previously processed data may be lost.

And on p.168:

The critical question we have to ask is what happens when an exception is thrown in the middle of processing a list of IATA codes. Exceptions blow up the call stack—plain and simple. If you carefully examine the code, you’ll see we are not catching the exception anywhere. So, it will result in abrupt termination of the program.

But that is the whole idea. You do want to discard (rather than blow up) all the contexts of the call stack that have become invalidated by the exception: everything up to the catch. It happens in all cases where one “throws”, whether in a stream pipeline or elsewhere. The one problem is that there is no nice place to set up “finally” handlers in a pipeline, but then again, resources should be opened & closed around the pipeline, not inside.

Examples:

ML has exceptions:

https://courses.cs.washington.edu/courses/cse341/04wi/lectures/10-ml-exceptions.html

Clojure has exceptions, they are exactly the JVM exceptions of course (probably slight different in ClojureScript, but I never looked into this)

https://clojuredocs.org/clojure.core/try

Scheme has exceptions

https://courses.cs.washington.edu/courses/cse341/04wi/lectures/15-scheme-continuations.html

Even languages with an unusual (but very powerful) model of computation like Prolog can have exceptions (although not in the - quite ancient - ISO Prolog standard), here is SWI-Prolog:

https://www.swi-prolog.org/pldoc/man?predicate=throw/1

As long as there is a concept of “a tree of computations” than can be torn down an rolled back, you can have them but: the meaning of “throwing an exception” may mesh inelegantly with the language and lead to various restrictions. And one always has to handle the two aspects in one’s head: what the program computes and how it is actually evaluated by the machine, virtual or otherwise. One could look at this as another example of a “leaky abstraction”: the state machine embedded in a real, failing world, peeks through the abstraction of a functional language exisiting in an idealistic universe layered on top.

The Monadic approach

When applied to stream pipelines, this is “Dealing with it Downstream”

The “Monadic” approach seems to be exactly the one promoted in Chapter 10. Here we create a construct similar to Java’s Optional<T> carrying either the result of a computation or information about the exceptional condition that occurred. On page 173, it is said that “we need a Union Type”, which is looking at the problem from the implementation side of course. We need an Exceptional<T>, here called Try<T>.

If a stage in the pipeline receives a Failure instead of receiving a Success, the function in that stage will merely pass along that failure downstream without processing.

In other words, applying a function to an instance of type Try<T> behaves like the identity.

Haskell seems to handle exceptions “the monadic way”

https://wiki.haskell.org/Exception

In

Govindarajan, R. (1993). Exception handlers in functional programming languages. IEEE Transactions on Software Engineering, 19(8), 826–834. doi:10.1109/32.238585

which promotes the same idea (“a shielded value”) but also adds “cure handlers” to manage locally-fixable problem, I found this text:

A key issue in program construction is robustness. Software reliability can be achieved by the judicious use of fault-tolerant tools. Exception handling is one of the two techniques used for developing reliable software.

Bretz and Ebert identify two problems in supporting exception handling constructs in functional programming. An exception, in imperative languages, is treated as a means of effecting a control transfer. Hence, there is a fundamental conflict between the functional approach followed in functional languages and the control flow-oriented view of exceptions.

Due to this, exceptions in functional languages can result in nondeterministic behavior of the FP program. For example, if there are two exception points inside a given function, the result of parallel evaluation of the function could be different, depending on which exception is raised first. This problem has been considered as intrinsic to incorporating exceptions in functional languages. Languages ML and ALEX circumvent this problem by proposing sequential execution for FP. Such a restriction is severe and is essentially required because the control flow view of exceptions has been carried through to functional languages. We discard this view and define the semantics of FP functions operating on exception objects without imposing any restriction on the execution.

Secondly, exception handling might cause side effects in expressions and hence might violate the property of referential transparency. Proposed solutions suggest the association of an environment with the functions. But in our case, discarding the conventional control flow view of exception solves this problem naturally.

Reeves et al. point out that embedding exception handling constructs in lazy functional languages can transform nonstrict functions into hyper-strict functions. (…) This is because both subexpressions need to be evaluated to determine whether or not they raise any exception. Reeves et al. claim the transformation of nonstrict actors into hyper-strict actors is due to the up-propagation of signals through nonstrict operators. To overcome this problem, the notion of down-propagation and firewalls has been defined (…). In this paper, however, we argue that it is not the up-propagation, but the persistent nature of exception values that causes the above problem.

As previously mentioned, Philip Wadler on this in a quite mathematical notation to express M<T> in “Monads for Functional Programming”, which can be found here:

https://homepages.inf.ed.ac.uk/wadler/topics/monads.html

Where Next?

Popular Pragmatic Bookshelf topics Top

johnp
Hi Brian, Looks like the api for tinydb has changed a little. Noticed while working on chapter 7 that the .purge() call to the db throws...
New
ianwillie
Hello Brian, I have some problems with running the code in your book. I like the style of the book very much and I have learnt a lot as...
New
Alexandr
Hi everyone! There is an error on the page 71 in the book “Programming machine learning from coding to depp learning” P. Perrotta. You c...
New
mikecargal
Title: Hands-On Rust (Chap 8 (Adding a Heads Up Display) It looks like ​.with_simple_console_no_bg​(SCREEN_WIDTH*2, SCREEN_HEIGHT*2...
New
mikecargal
Title: Hands-On Rust (Chapter 11: prefab) Just played a couple of amulet-less games. With a bit of debugging, I believe that your can_p...
New
JohnS
I can’t setup the Rails source code. This happens in a working directory containing multiple (postgres) Rails apps. With: ruby-3.0.0 s...
New
patoncrispy
I’m new to Rust and am using this book to learn more as well as to feed my interest in game dev. I’ve just finished the flappy dragon exa...
New
AndyDavis3416
@noelrappin Running the webpack dev server, I receive the following warning: ERROR in tsconfig.json TS18003: No inputs were found in c...
New
brunogirin
When trying to run tox in parallel as explained on page 151, I got the following error: tox: error: argument -p/–parallel: expected one...
New
a.zampa
@mfazio23 I’m following the indications of the book and arriver ad chapter 10, but the app cannot be compiled due to an error in the Bas...
New

Other popular topics Top

AstonJ
If it’s a mechanical keyboard, which switches do you have? Would you recommend it? Why? What will your next keyboard be? Pics always w...
New
ohm
Which, if any, games do you play? On what platform? I just bought (and completed) Minecraft Dungeons for my Nintendo Switch. Other than ...
New
dasdom
No chair. I have a standing desk. This post was split into a dedicated thread from our thread about chairs :slight_smile:
New
New
PragmaticBookshelf
Rust is an exciting new programming language combining the power of C with memory safety, fearless concurrency, and productivity boosters...
New
AstonJ
In case anyone else is wondering why Ruby 3 doesn’t show when you do asdf list-all ruby :man_facepalming: do this first: asdf plugin-upd...
New
PragmaticBookshelf
Build highly interactive applications without ever leaving Elixir, the way the experts do. Let LiveView take care of performance, scalabi...
New
AstonJ
Biggest jackpot ever apparently! :upside_down_face: I don’t (usually) gamble/play the lottery, but working on a program to predict the...
New
PragmaticBookshelf
Author Spotlight Jamis Buck @jamis This month, we have the pleasure of spotlighting author Jamis Buck, who has written Mazes for Prog...
New
AnfaengerAlex
Hello, I’m a beginner in Android development and I’m facing an issue with my project setup. In my build.gradle.kts file, I have the foll...
New

Latest in Functional Programming in Java, Second Edition

Functional Programming in Java, Second Edition Portal

Sub Categories: