Last active
March 3, 2016 01:16
-
-
Save tmspzz/dba514418f7408de1d8b to your computer and use it in GitHub Desktop.
ListResult<E,R> represents a series of computations that have succeeded or failed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ListResult.swift | |
// | |
// Created by Tommaso Piazza on 23/02/16. | |
import Foundation | |
import Swiftz | |
/** | |
`ListResult<E,R>` represents a list of results of type `R` and errors `E`. | |
`.errors` stores the errors while `.results` stores the results. | |
A `ListResults<NSError,String>` would present the outcome of a series of computations that | |
have either produced a `NSError` or produced a `String`. | |
The naked type `ListResult<E,R>` already represents it's generic types stored in a lists. | |
In this case ListResult.errors has type `[NSError]` and ListResult.results has type `[String]` | |
A `ListResult<[NSError], [String]>` would represent the outcome of a series of computations | |
that have either produced an array of `NSError` or produced an array of `Strings`. | |
In this case `ListResult.errors` has type `[[NSError]]` and ListResult.results has type `[[String]]` | |
- important: | |
To perform a series of transformations on `.results` and store the errors of such transformations | |
one has to stay within the `ListResult<E,R>` Monad. | |
(listResult: ListResult<E,R>) | |
>>- (transformation1: R -> ListResult<E,T1>) | |
>>- (transformation2: T1 -> ListResult<E,T2>) | |
will transform from `R` to `T2` and the errors `E`. | |
- attention: | |
Using `ListResult<E,R>` in the applicative style will only transform the results `R` | |
without stacking any errors. This is because of the applicative definition | |
<*> <E, A, B>(f : ListResult<E, A -> B>, e : ListResult<E, A>) -> ListResult<E, B> | |
where `A->B` does not produce errors. | |
*/ | |
public struct ListResult<E, R> { | |
public typealias A = R | |
var errors:[E] | |
var results:[R] | |
init(errors:[E], results:[R]) { | |
self.errors = errors | |
self.results = results | |
} | |
/// Named function for `>>-`. It simply applies the function `f` to `R` and returns | |
/// a new `ListResult` with the errors `E` accumutaled in `.errors` and results `R` | |
/// accumutaled in `.results` | |
public func flatMap<S>(f: R -> ListResult<E, S>) -> ListResult<E, S> { | |
return self >>- f | |
} | |
/// A `ListResult<E, R>` is empty when | |
/// both `.errors` and `.result` | |
/// are empty. | |
var isEmpty:Bool { | |
return self.results.isEmpty && self.errors.isEmpty | |
} | |
/// A `ListResult<E, R>` has errors | |
/// when `.errors` is not empty | |
var hasErrors:Bool { | |
return !self.errors.isEmpty | |
} | |
/// A `ListResult<E, R>` has results | |
/// when `.results` is not empty | |
var hasResults:Bool { | |
return !self.results.isEmpty | |
} | |
/// A `ListResult<E, R>` is succesful | |
/// when it is **not** empty and does **not** have errors | |
/// - seealso: hasErrors, isEmpty | |
var isSuccessful: Bool { | |
return !self.hasErrors && !self.isEmpty | |
} | |
} | |
public func <*> <E, A, B>(f : ListResult<E, A -> B>, l : ListResult<E, A>) -> ListResult<E, B> { | |
let newErrors = f.errors + l.errors | |
return ListResult<E, B>(errors:newErrors, results:f.results <*> l.results) | |
} | |
/// Fmap | Applies a function to all `.results` values contained in the given `ListResult<L,R>` | |
/// leaving the `.errors` untouched | |
public func <^> <E, A, B>(f : A -> B, l : ListResult<E, A>) -> ListResult<E, B> { | |
let bs = l.results.map { | |
f($0) | |
} | |
return ListResult<E, B>(errors: l.errors, results: bs) | |
} | |
public func >>- <E, R, S>(l : ListResult<E, R>, f : R -> ListResult<E, S>) -> ListResult<E, S> { | |
let reductionElement:ListResult<E,S> = ListResult<E, S>(errors: [], results: []) | |
let l = l.results.map { | |
return f($0) | |
}.reduce(reductionElement){ (rElement:ListResult<E,S>, listResult:ListResult<E, S>) in | |
return rElement <> listResult | |
} | |
return l | |
} | |
//MARK: - Swiftz Pointed | |
extension ListResult: Pointed { | |
public static func pure(r: A) -> ListResult<E,A> { | |
let lr:ListResult<E, A> = ListResult<E, A>(errors: [], results: [r]) | |
return lr | |
} | |
} | |
//MARK: - Swiftz Functor | |
extension ListResult: Functor { | |
public typealias B = Any | |
public typealias FB = ListResult<E, B> | |
public func fmap<B>(f: R -> B) -> ListResult<E, B> { | |
return f <^> self | |
} | |
} | |
//MARK: - Swiftz Semigroup | |
extension ListResult: Semigroup { | |
/// An associative binary operator. | |
public func op(other: ListResult) -> ListResult { | |
return ListResult(errors: self.errors + other.errors, results: self.results + other.results) | |
} | |
} | |
//MARK: - Swiftz Monoid | |
extension ListResult: Monoid { | |
public static var mempty: ListResult { | |
return ListResult(errors: [], results: []) | |
} | |
} | |
//MARK: - Swiftz Applicative | |
extension ListResult: Applicative { | |
public typealias FAB = ListResult<E, A -> B> | |
public func ap(f: ListResult.FAB) -> ListResult.FB { | |
return f <*> self | |
} | |
} | |
//MARK: - Swiftz Monad | |
extension ListResult: Monad { | |
public func bind<B>(f: R -> ListResult<E, B>) -> ListResult<E, B> { | |
return self >>- f | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment