3 ms·
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 sparkie 12y ago
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.