Created
November 16, 2017 16:53
-
-
Save mbrandonw/7aab415312379022e60f3a9a107b6792 to your computer and use it in GitHub Desktop.
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
// There's a natural way to lift a predicate `(A) -> Bool` to a function `(A) -> A? | |
func optionalBool<A>(_ p: @escaping (A) -> Bool) -> (A) -> A? { | |
return { p($0) ? .some($0) : nil } | |
} | |
// There's a natural way to lift a predicate `(A) -> Bool` to a function `(A) -> Either<A, A> | |
func eitherBool<A>(_ p: @escaping (A) -> Bool) -> (A) -> Either<A, A> { | |
// notice the similarities of this with `optionalBool`: | |
return { p($0) ? .right($0) : .left($0) } | |
} | |
extension Array { | |
// An implementation of `filterMap`. | |
func filterMap<B>(_ f: (Element) -> B?) -> [B] { | |
return self.flatMap(f) // <- insert yer favorite implementation here! | |
} | |
// Deriving `filter` from `filterMap` | |
func _filter(_ p: @escaping (Element) -> Bool) -> Array { | |
return filterMap(optionalBool(p)) | |
} | |
// Partition an array into two arrays taking *different* values than what this array holds. This can be | |
// useful to split an array into two based on a mapping function. For example, if we had an array of | |
// `Result` values we could split it into the array of errors and the array of values. | |
func _partitionMap<A, B>(_ p: @escaping (Element) -> Either<A, B>) -> (left: [A], right: [B]) { | |
var result = (left: [A](), right: [B]()) | |
for x in self { | |
switch p(x) { | |
case let .left(a): | |
result.left.append(a) | |
case let .right(b): | |
result.right.append(b) | |
} | |
} | |
return result | |
} | |
// Partition an array into two arrays, one where the predicate is `true` and one where it is `false`. | |
func _partition(_ p: @escaping (Element) -> Bool) -> (`true`: Array, `false`: Array) { | |
var result = (true: Array(), false: Array()) | |
for x in self { | |
p(x) | |
? result.true.append(x) | |
: result.false.append(x) | |
} | |
return result | |
// NB: can also be implemented in terms of `_partitionMap`. Notice the similarities of this | |
// with the implementation of `_filter` in terms of `filterMap`. | |
return _partitionMap(eitherBool(p)) | |
} | |
} | |
// `Either<A, B>` is a natural generalization of `Optional<B>` in which we don't only model the absence of | |
// a value of `B`, but also allow substituting in a value of `A` in the case `B` is missing. | |
enum Either<A, B> { | |
case left(A) | |
case right(B) | |
} | |
// Also see the purescript implementation for more fun ideas: | |
// https://github.com/LiamGoodacre/purescript-filterable/blob/master/src/Data/Filterable.purs#L56-L66 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment