I wrote this up out of necessity, then realized that I actually needed to write it in PHP. Just so I didn't waste the last hour, I'd like a review of it. My concerns:
- This is my first time using the module pattern. Am I implementing it correctly?
- To restrict the creation of
Either
objects, I'm usingSuccess
andFailure
as pseudo-axillary constructors to ensure that only one of Either's parameters is a meaningful value. Since they aren't real constructors though, they can't instantiate anObject
, so I had to put thenew
inside ofSuccess
andFailure
instead of expecting the caller to use it. Is this ok? Is there any way around it? - Since I'm restricting the creation of
Either
s, should I be checking the parameters, or just assume they're ok? - Anything else. I've half moved on to writing PHP for the time being, but any suggestions on how to improve my JavaScript would be appreciated.
Example:
function someOperationReturningEither() { return Either.Success('Success'); } someOperationReturningEither().then( function(successValue) { console.log('Succeeded!'); }, function(error) { console.log('Failure'); } );
var Either = (function() {
/**
* Represents either a value from a successful operation, or an error from one that failed
* Only one of the paramters should be a valid value. The other must be null.
*
* @param {Object} succeeded The return of a successful operation, or null if failed
* @param {Object} failed An object representing an error if the operation failed, or null if succeeded
* @constructor
*/
var Option = function(succeeded, failed) {
var success = succeeded;
var fail = failed;
if (!hasValidState()) {
throw invalidOptionStateError();
}
/**
* Checks if the operation was successful
*
* @returns {boolean}
*/
this.succeeded = function() {
return succeeded !== null;
};
/**
* Checks if the operation failed
*
* @returns {boolean}
*/
this.failed = function() {
return failed !== null;
};
/**
* Calls the first callback if the operation was successful, otherwise the second callback is called
* @param {SuccessCallback} onSuccess
* @param {FailureCallback} onFail
*
* @returns {*} The return from the called callback
*/
this.then = function(onSuccess, onFail) {
if (this.succeeded()) {
return onSuccess(success);
} else if (this.failed()) {
return onFail(fail);
} else {
throw new invalidOptionStateError();
}
};
this.toString = function() {
return "(" + success + ", " + fail + ")";
};
/**
* Checks if only one of the options is given
*
* @returns {boolean}
*/
function hasValidState() {
return Boolean((success !== null) ^ (fail !== null));
}
/**
* An Error reporting an invalid option state)
* @returns {Error}
*/
function invalidOptionStateError() {
return new Error("Invalid Option state");
}
};
return {
/**
* A wrapper over a successful value
*
* @param value The value to return
* @returns {Option} An Option representing a successful operation
*/
Success: function(value) {
return new Option(value, null);
},
/**
*
* @param error An object representing a failed operation
* @returns {Option}
* @constructor
*/
Failure: function(error) {
return new Option(null, error);
}
};
/**
* @type SuccessCallback
* @function
*
* @param {*} The return value of the operation
* @returns {*}
*/
/**
* @type FailureCallback
* @function
*
* @param {*} The error returned by the operation
* @returns {*}
*/
})();