// Copyright (c) 2013-2014 Quildreen Motta <quildreen@gmail.com>
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation files
// (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/**
* @module lib/validation
*/
module.exports = Validation
// -- Aliases ----------------------------------------------------------
var clone = Object.create
var unimplemented = function(){ throw new Error('Not implemented.') }
var noop = function(){ return this }
// -- Implementation ---------------------------------------------------
/**
* The `Validation[α, β]` is a disjunction that's more appropriate for
* validating inputs, or any use case where you want to aggregate failures. Not
* only does the `Validation` provide a better terminology for working with
* such cases (`Failure` and `Success` versus `Failure` and `Success`), it also
* allows one to easily aggregate failures and successes as an Applicative
* Functor.
*
* @class
* @summary
* Validation[α, β] <: Applicative[β]
* , Functor[β]
* , Show
* , Eq
*/
function Validation() { }
Failure.prototype = clone(Validation.prototype)
function Failure(a) {
this.value = a
}
Success.prototype = clone(Validation.prototype)
function Success(a) {
this.value = a
}
// -- Constructors -----------------------------------------------------
/**
* Constructs a new `Validation[α, β]` structure holding a `Failure` value.
*
* @summary a → Validation[α, β]
*/
Validation.Failure = function(a) {
return new Failure(a)
}
Validation.prototype.Failure = Validation.Failure
/**
* Constructs a new `Etiher[α, β]` structure holding a `Success` value.
*
* @summary β → Validation[α, β]
*/
Validation.Success = function(a) {
return new Success(a)
}
Validation.prototype.Success = Validation.Success
// -- Conversions ------------------------------------------------------
/**
* Constructs a new `Validation[α, β]` structure from a nullable type.
*
* Takes the `Failure` case if the value is `null` or `undefined`. Takes the
* `Success` case otherwise.
*
* @summary α → Validation[α, α]
*/
Validation.fromNullable = function(a) {
return a != null? this.Success(a)
: /* otherwise */ this.Failure(a)
}
Validation.prototype.fromNullable = Validation.fromNullable
/**
* Constructs a new `Either[α, β]` structure from a `Validation[α, β]` type.
*
* @summary Either[α, β] → Validation[α, β]
*/
Validation.fromEither = function(a) {
return a.fold(this.Failure.bind(this), this.Success.bind(this))
}
// -- Predicates -------------------------------------------------------
/**
* True if the `Validation[α, β]` contains a `Failure` value.
*
* @summary Boolean
*/
Validation.prototype.isFailure = false
Failure.prototype.isFailure = true
/**
* True if the `Validation[α, β]` contains a `Success` value.
*
* @summary Boolean
*/
Validation.prototype.isSuccess = false
Success.prototype.isSuccess = true
// -- Applicative ------------------------------------------------------
/**
* Creates a new `Validation[α, β]` instance holding the `Success` value `b`.
*
* `b` can be any value, including `null`, `undefined` or another
* `Validation[α, β]` structure.
*
* @summary β → Validation[α, β]
*/
Validation.of = function(a) {
return this.Success(a)
}
Validation.prototype.of = Validation.of
/**
* Applies the function inside the `Success` case of the `Validation[α, β]` structure
* to another applicative type.
*
* The `Validation[α, β]` should contain a function value, otherwise a `TypeError`
* is thrown.
*
* @method
* @summary (@Validation[α, β → γ], f:Applicative[_]) => f[β] → f[γ]
*/
Validation.prototype.ap = unimplemented
Failure.prototype.ap = function(b) {
return b.isFailure? this.Failure(this.value.concat(b.value))
: /* otherwise */ this
}
Success.prototype.ap = function(b) {
return b.isFailure? b
: /* otherwise */ b.map(this.value)
}
// -- Functor ----------------------------------------------------------
/**
* Transforms the `Success` value of the `Validation[α, β]` structure using a regular
* unary function.
*
* @method
* @summary (@Validation[α, β]) => (β → γ) → Validation[α, γ]
*/
Validation.prototype.map = unimplemented
Failure.prototype.map = noop
Success.prototype.map = function(f) {
return this.of(f(this.value))
}
// -- Show -------------------------------------------------------------
/**
* Returns a textual representation of the `Validation[α, β]` structure.
*
* @method
* @summary (@Validation[α, β]) => Void → String
*/
Validation.prototype.toString = unimplemented
Failure.prototype.toString = function() {
return 'Validation.Failure(' + this.value + ')'
}
Success.prototype.toString = function() {
return 'Validation.Success(' + this.value + ')'
}
// -- Eq ---------------------------------------------------------------
/**
* Tests if an `Validation[α, β]` structure is equal to another `Validation[α, β]`
* structure.
*
* @method
* @summary (@Validation[α, β]) => Validation[α, β] → Boolean
*/
Validation.prototype.isEqual = unimplemented
Failure.prototype.isEqual = function(a) {
return a.isFailure && (a.value === this.value)
}
Success.prototype.isEqual = function(a) {
return a.isSuccess && (a.value === this.value)
}
// -- Extracting and recovering ----------------------------------------
/**
* Extracts the `Success` value out of the `Validation[α, β]` structure, if it
* exists. Otherwise throws a `TypeError`.
*
* @method
* @summary (@Validation[α, β]) => Void → β :: partial, throws
* @see {@link module:lib/validation~Validation#getOrElse} — A getter that can handle failures.
* @see {@link module:lib/validation~Validation#merge} — The convergence of both values.
* @throws {TypeError} if the structure has no `Success` value.
*/
Validation.prototype.get = unimplemented
Failure.prototype.get = function() {
throw new TypeError("Can't extract the value of a Failure(a).")
}
Success.prototype.get = function() {
return this.value
}
/**
* Extracts the `Success` value out of the `Validation[α, β]` structure. If the
* structure doesn't have a `Success` value, returns the given default.
*
* @method
* @summary (@Validation[α, β]) => β → β
*/
Validation.prototype.getOrElse = unimplemented
Failure.prototype.getOrElse = function(a) {
return a
}
Success.prototype.getOrElse = function(_) {
return this.value
}
/**
* Transforms a `Failure` value into a new `Validation[α, β]` structure. Does nothing
* if the structure contain a `Success` value.
*
* @method
* @summary (@Validation[α, β]) => (α → Validation[γ, β]) → Validation[γ, β]
*/
Validation.prototype.orElse = unimplemented
Success.prototype.orElse = noop
Failure.prototype.orElse = function(f) {
return f(this.value)
}
/**
* Returns the value of whichever side of the disjunction that is present.
*
* @summary (@Validation[α, α]) => Void → α
*/
Validation.prototype.merge = function() {
return this.value
}
// -- Folds and Extended Transformations -------------------------------
/**
* Applies a function to each case in this data structure.
*
* @method
* @summary (@Validation[α, β]) => (α → γ), (β → γ) → γ
*/
Validation.prototype.fold = unimplemented
Failure.prototype.fold = function(f, _) {
return f(this.value)
}
Success.prototype.fold = function(_, g) {
return g(this.value)
}
/**
* Swaps the disjunction values.
*
* @method
* @summary (@Validation[α, β]) => Void → Validation[β, α]
*/
Validation.prototype.swap = unimplemented
Failure.prototype.swap = function() {
return this.Success(this.value)
}
Success.prototype.swap = function() {
return this.Failure(this.value)
}
/**
* Maps both sides of the disjunction.
*
* @method
* @summary (@Validation[α, β]) => (α → γ), (β → δ) → Validation[γ, δ]
*/
Validation.prototype.bimap = unimplemented
Failure.prototype.bimap = function(f, _) {
return this.Failure(f(this.value))
}
Success.prototype.bimap = function(_, g) {
return this.Success(g(this.value))
}
/**
* Maps the failure side of the disjunction.
*
* @method
* @summary (@Validation[α, β]) => (α → γ) → Validation[γ, β]
*/
Validation.prototype.failureMap = unimplemented
Success.prototype.failureMap = noop
Failure.prototype.failureMap = function(f) {
return this.Failure(f(this.value))
}
/**
* Maps the failure side of the disjunction.
*
* @method
* @deprecated in favour of {@link module:lib/validation~Validation#failureMap}
* @summary (@Validation[α, β]) => (α → γ) → Validation[γ, β]
*/
Validation.prototype.leftMap = Validation.prototype.failureMap