Skip to content

Instantly share code, notes, and snippets.

@tmspzz
Last active March 3, 2016 01:16
Show Gist options
  • Save tmspzz/dba514418f7408de1d8b to your computer and use it in GitHub Desktop.
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.
//
// 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