// 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