Regarding Wrappers Around Effectual Foreign Functions

In packages like node-fs, the wrapper functions don’t have return types to explicitly indicate fallibility, e.g. Effect (Either FileError FileDescriptor) for fdOpen; exceptions will instead be thrown from the foreign functions that which they encapsulate, and it’s incumbent upon the programmer to use try and friends if that’s not desirable.

Given this, is it considered permissible for wrapper functions to ignore these exceptions that are raised in connection to the “outside world,” and only obviate or handle those exceptions that are thrown to uphold invariants related to the foreign function’s parameters?

As a somewhat contrived example, consider a sendEmail foreign function that will throw if it can’t transmit the email, or the supplied addresses are neither strings nor properly formatted. A wrapper could either use a newtype and a smart constructor to ensure the addresses are valid prior to calling the foreign function, or catch the exception related to validation and return, say, Left InvalidAddress; however, an exception that arose due to, for example, the inability to make a TCP connection, is passed through and not communicated via the return value.

By whom?

It needn’t be unsound, if that’s what you’re asking, as long as the types are used appropriately and any side effects are still wrapped in an Effect thunk. It may or may not be the best API for your use case, but I can’t anticipate that without knowing what you want to do with it. Either way, no PureScript police will stop you from implementing your vision.

“Permissible” was a poor choice of a word, apologies. Rather, I’d like to know whether my presumptions stated in the OP are reasonable in the context of any best practices regarding the handling of exceptions thrown by foreign functions.

 I can’t anticipate that without knowing what you want to do with it.

I’m wrapping a database client library. So some exceptions can be obviated by using the right types for the wrapper function’s parameters, but others cannot, and I am attempting to determine whether I should leave it up to the user to use try or force an Either upon them.

The design you sketched seems reasonable, sure.

There are two special features of exceptions: they are dynamic (you don’t have to account for all—or even any—of the possible exceptions that arise in order to write well-typed code) and they are non-local (if module A uses module B uses module C, and module C throws an exception, A can handle it without B having to support that). If those features would be useful to you in your use case, use exceptions. Most language features in PureScript are static and local, and so an API that relies on exceptions is going to look somewhat non-idiomatic. But PureScript supports exceptions because sometimes they’re what you want as a user, and sometimes the primary goal of a library is not to be completely PureScript-idiomatic.

node-fs is a thin wrapper and not necessarily representative of best practices for writing a PureScript library ex nihilo. But the thing about wrapping an existing API is that you can either replicate it faithfully, or you can implement a different model on top of it and require your users to learn that. For well-known, well-documented interfaces like node.js’s, it often makes more sense to have a lower layer that just provides types and maybe some very lightweight adapters, and an upper layer (possibly more than one, possibly implemented later by someone else) that implements a more PureScript-flavored model on top, because the upper layer is going to be a less stable API.

So do your users want a familiar API? Do they want a stable API? Do they want an API that feels of a piece with most of the rest of the PureScript ecosystem? Will they be using other low-level APIs that use exceptions and want to handle all exceptional cases in one place? Will they expect to recover from issues locally and want to work with the most explicit representation of those issues? These and similar questions should guide your design choices.

2 Likes

That was quite helpful, and what I needed to read. I appreciate your taking some of your time to expound these considerations, especially given how my opening post poorly conveys the heart of the matter.

Thank you, and I hope you have a blessed day.

1 Like