Skip to content

Instantly share code, notes, and snippets.

@mbrandonw
Created November 16, 2017 16:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mbrandonw/7aab415312379022e60f3a9a107b6792 to your computer and use it in GitHub Desktop.
Save mbrandonw/7aab415312379022e60f3a9a107b6792 to your computer and use it in GitHub Desktop.
// 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