Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.



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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: