

How to design an API function that creates something - oflordal
http://sheredom.wordpress.com/2014/08/10/how-to-design-api-function-that-creates-something/

======
aurelianito
This article can be seen as a very good explanation of why exceptions are a
valuable feature for a language. In python, mentioning one of the languages
exposed in the article, I would return the created object and throw an
exception on error.

~~~
sparkie
This has the same properties as B in his examples though - one can simply fail
to catch the exception and the same problems arise. I actually much prefer the
B solution (or a variation, where instead of returning error codes, you pass
in an exception handler function), as opposed to exceptions which are "hidden"
from the API and rely on users to read documentation (hint: most people don't,
until they find out the hard way).

~~~
aurelianito
It is different. An excepction decouples the place where errors must be
handled from where the required object will be used. A closer example would be
if the function received a function pointer or, even better, a continuation to
be invoked if an error ocurrs.

------
lyinsteve
I'm personally fond of filling an error buffer, but a bit more explicitly than
just an int.

    
    
        typedef struct APIError {
            int code;
            char *description;
        };
    
        /**
         * Returns NULL and fills the error parameter if creation was unsuccessful.
         */
        Something APICreateSomething(bool param1, int param2, APIError *error);
    

So common execution would be:

    
    
        APIError error;
        Something something = APICreateSomething(true, 3, &error);
    
        if (error) {
           // handle error...
        }
    

And if the user passed in NULL for the error parameter, then that's their
problem, and they won't get error information.

~~~
TillE
If that char* is just a pointer into a static array which maps error codes to
strings, I'd generally prefer to use a separate API to do that.

If it's a freshly allocated string that needs to be freed by the caller, well,
that's one of many reasons I never enjoy my time with C.

------
neilunadkat12
I think something that he has missed is the design of passing functions as
params for success and failures. Of course these are generally used when there
are external API calls which are async, but nowadays those kind of API calls
are quite common.

void createSomething(bool param1,int param2, Something something, success:
function (something) {}, error: function (error){});

Of course this would be in those languages that support passing of functions
as params.

------
Strilanc
One of the unmentioned advantages of style "A", passing in a pointer to where
you want the result, is that the caller can trivially switch allocators if
they need that speed (e.g. a specialized slab allocator is much faster than
malloc). If you want to support this with the other styles, you end up adding
a parameter (or generic parameter) for the allocator.

Style "E" is also known as an error monad, and has a lot of advantages when
writing functional-style code. For example, you can use the usual List.map on
a function that returns an error monad but not on one that has out parameters
or throws exceptions.

~~~
sparkie
I'd argue that adding the parameter for the allocator is almost always what
you want to do anyway - the idea of the caller allocating the memory to be
filled in by the constructor completely violates the idea of encapsulation -
and it basically assumes that the memory required by the object is contiguous,
of known size, or that the caller knows the exact layout - usually not the
case.

For example, if I gave you the following API and expected you to use the
constructor (requiring pre-allocated list), how would you begin?

    
    
        struct list_t;
        int list_new(list_t*);
        int list_add(list_t*, Object);
        int list_free(list_t*);
    

You have no idea whether I've implemented a linked list, an array, vector,
vlist, or anything else - you simply don't know how to allocate the required
memory.

A constructor taking a pointer to an already allocated object can serve one
useful function though - it can be the equivalent of a copy constructor in
plain C. You still need the second argument or return value for the new object
though, so the above API example is unfit for purpose.

On the "error monad", I wish you wouldn't call it this, because it's
potentially scaring away people who don't understand what a monad is, or think
it is something complex. Error (or Either) in Haskell has nothing inherently
to do with monads - it's just a data type with two constructors, one to
construct successful values, and another to construct errors. The example you
gave, of using "map" has nothing to do with a monad either, but is just a
plain Functor for the Error type. Sure, there exist an instance of Monad for
Error/Either, and they're extremely useful, but that's not all there is - it
shouldn't be defined as "error monad".

The real value of the Error/Either type comes from the fact that it's a tagged
union, where only one of the constructors can be inhabited at any time, and to
find out which, you must pattern match over the possibilities, with the
compiler warning you if you don't do so exhaustively. Functional languages
also don't allow you to simply ignore result of a "function call".

Attempts to simulate this like style E in the blog post fall short, because
one can simply ignore the result or the "errorCode" and attempt to use the
object instead - and the compiler won't complain. It's not that these
languages don't have monads or functors - it's that they don't have the unit
type, tagged unions and (exhaustive) pattern matching. Monad and functor
instances for Error are bonuses once these are in place.

------
adamnemecek
I checked out the LLVM source [0] and what they do is slightly different
(their is a tagged union) since their HasError is not part of the union which
makes more sense since it does not rely on memory alignment. It does not
really matter since it seems that the values in the struct are aligned
appropriately anyway but it'd still be pretty wonky if there was just the
union.

[0] [https://github.com/llvm-
mirror/llvm/blob/eee7a7a8362afddab3f...](https://github.com/llvm-
mirror/llvm/blob/eee7a7a8362afddab3fd9bf10b7023da7e7c42e5/include/llvm/Support/ErrorOr.h#L265)

------
fndrplayer13
I really enjoyed this article. I think I personally prefer the struct return.
It's explicit, if maybe a tiny bit wasteful of memory. Certainly there will be
people whom this affects, but I think its good overall.

------
dfbrown
With tuples in C++11 you can have multiple return values and ignore ones you
don't want: [http://ideone.com/3eHOld](http://ideone.com/3eHOld)

------
th0br0
So he discovered the Either monad (?!)...

