Repository: gcanti/fp-ts
Pull request #493 (2018-06-25 08:35:25)

Feature/filterable instances
Part of https://github.com/gcanti/fp-ts/pull/485

Adds:
  - `Filterable` instance for `Array`
  - `Filterable` instance for `Either` (`getFilterable` using `Monoid`)
  - `Filterable` instance for `Option`
  - `Filterable` instance for `Validation` (`getFilterable` using `Monoid`)
  - `Filterable` instance for `StrMap`
  -  tests

Changes:
  - ~`Compactable`/`Filterable` default methods signature for `-2C` and `-3C` to require `_L` and `_U`~

Removes:
  - default implementations for `Compactable` methods
  - default implementations for `Filterable` methods


Patch:
diff --git a/src/Array.ts b/src/Array.ts
index 925c4d59..0fea391a 100644
--- a/src/Array.ts
+++ b/src/Array.ts
@@ -14,8 +14,9 @@ import { Plus1 } from './Plus'
 import { Setoid, getArraySetoid } from './Setoid'
 import { Traversable1 } from './Traversable'
 import { Unfoldable1 } from './Unfoldable'
-import { Endomorphism, Predicate, Refinement, concat, identity, tuple } from './function'
+import { Endomorphism, Predicate, Refinement, concat, tuple } from './function'
 import { Compactable1, Separated } from './Compactable'
+import { Filterable1 } from './Filterable'
 
 // Adapted from https://github.com/purescript/purescript-arrays
 
@@ -180,25 +181,6 @@ const extend = <A, B>(fa: Array<A>, f: (fa: Array<A>) => B): Array<B> => {
   return fa.map((_, i, as) => f(as.slice(i)))
 }
 
-/**
- * @function
- * @since 1.0.0
- */
-export const partitionMap = <A, L, R>(fa: Array<A>, f: (a: A) => Either<L, R>): { left: Array<L>; right: Array<R> } => {
-  const left: Array<L> = []
-  const right: Array<R> = []
-  const len = fa.length
-  for (let i = 0; i < len; i++) {
-    const v = f(fa[i])
-    if (v.isLeft()) {
-      left.push(v.value)
-    } else {
-      right.push(v.value)
-    }
-  }
-  return { left, right }
-}
-
 /**
  * @example
  * flatten([[1], [2], [3]]) // [1, 2, 3]
@@ -521,23 +503,6 @@ export const findLast = <A>(as: Array<A>, predicate: Predicate<A>): Option<A> =>
   return fromNullable(a)
 }
 
-/**
- * Filter an array, keeping the elements which satisfy a predicate function, creating a new array
- * @function
- * @since 1.0.0
- */
-export const filter = <A>(as: Array<A>, predicate: Predicate<A>): Array<A> => {
-  const l = as.length
-  const r = []
-  for (let i = 0; i < l; i++) {
-    const v = as[i]
-    if (predicate(v)) {
-      r.push(v)
-    }
-  }
-  return r
-}
-
 /**
  * @function
  * @since 1.0.0
@@ -635,33 +600,6 @@ export const reverse = <A>(as: Array<A>): Array<A> => {
   return copy(as).reverse()
 }
 
-/**
- * Apply a function to each element in an array, keeping only the results
- * which contain a value, creating a new array
- * @function
- * @since 1.0.0
- */
-export const mapOption = <A, B>(as: Array<A>, f: (a: A) => Option<B>): Array<B> => {
-  const r: Array<B> = []
-  const len = as.length
-  for (let i = 0; i < len; i++) {
-    const v = f(as[i])
-    if (v.isSome()) {
-      r.push(v.value)
-    }
-  }
-  return r
-}
-
-/**
- * Filter an array of optional values, keeping only the elements which contain a value, creating a new array
- * @function
- * @since 1.0.0
- */
-export const catOptions = <A>(as: Array<Option<A>>): Array<A> => {
-  return mapOption(as, identity)
-}
-
 /**
  * Extracts from a list of `Either` all the `Right` elements. All the `Right` elements are extracted in order
  * @function
@@ -803,8 +741,97 @@ export const sortBy1 = <A>(head: Ord<A>, tail: Array<Ord<A>>): Endomorphism<Arra
   return sort(tail.reduce(getSemigroup<A>().concat, head))
 }
 
-const compact = catOptions
-const separate = <L, A>(fa: Either<L, A>[]): Separated<L[], A[]> => partitionMap(fa, identity)
+const filterMap = <A, B>(fa: A[], f: (a: A) => Option<B>): B[] => {
+  const result: B[] = []
+  for (const a of fa) {
+    const optionB = f(a)
+    if (optionB.isSome()) {
+      result.push(optionB.value)
+    }
+  }
+  return result
+}
+/**
+ * @function
+ * @since 1.0.0
+ */
+export const partitionMap = <A, L, R>(fa: A[], f: (a: A) => Either<L, R>): Separated<L[], R[]> => {
+  const left: L[] = []
+  const right: R[] = []
+  for (const a of fa) {
+    const e = f(a)
+    if (e.isLeft()) {
+      left.push(e.value)
+    } else {
+      right.push(e.value)
+    }
+  }
+  return {
+    left,
+    right
+  }
+}
+/**
+ * Filter an array, keeping the elements which satisfy a predicate function, creating a new array
+ * @function
+ * @since 1.0.0
+ */
+export const filter = <A>(fa: A[], p: Predicate<A>): A[] => fa.filter(p)
+const partition = <A>(fa: A[], p: Predicate<A>): Separated<A[], A[]> => {
+  const left: A[] = []
+  const right: A[] = []
+  for (const a of fa) {
+    if (p(a)) {
+      right.push(a)
+    } else {
+      left.push(a)
+    }
+  }
+  return {
+    left,
+    right
+  }
+}
+const compact = <A>(fa: Option<A>[]): A[] => {
+  const result: A[] = []
+  for (const optionA of fa) {
+    if (optionA.isSome()) {
+      result.push(optionA.value)
+    }
+  }
+  return result
+}
+const separate = <RL, RR>(fa: Either<RL, RR>[]): Separated<RL[], RR[]> => {
+  const left: RL[] = []
+  const right: RR[] = []
+  for (const e of fa) {
+    if (e.isLeft()) {
+      left.push(e.value)
+    } else {
+      right.push(e.value)
+    }
+  }
+  return {
+    left,
+    right
+  }
+}
+
+/**
+ * Filter an array of optional values, keeping only the elements which contain a value, creating a new array
+ * Alias for {@link Compactable.compact}
+ * @function
+ * @since 1.0.0
+ */
+export const catOptions = compact
+
+/**
+ * Apply a function to each element in an array, keeping only the results which contain a value, creating a new array
+ * Alias for {@link Filterable.filterMap}
+ * @function
+ * @since 1.0.0
+ */
+export const mapOption = filterMap
 
 export const array: Monad1<URI> &
   Foldable1<URI> &
@@ -813,11 +840,16 @@ export const array: Monad1<URI> &
   Alternative1<URI> &
   Plus1<URI> &
   Extend1<URI> &
-  Compactable1<URI> = {
+  Compactable1<URI> &
+  Filterable1<URI> = {
   URI,
   map,
   compact,
   separate,
+  filter,
+  filterMap,
+  partition,
+  partitionMap,
   of,
   ap,
   chain,
diff --git a/src/Compactable.ts b/src/Compactable.ts
index 1fb0baf2..a5e6e894 100644
--- a/src/Compactable.ts
+++ b/src/Compactable.ts
@@ -1,9 +1,6 @@
 import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
-import { fromEither, Option } from './Option'
-import { Either, fromOption } from './Either'
-import { Functor, Functor1, Functor2, Functor2C, Functor3, Functor3C } from './Functor'
-import { Filterable, Filterable1, Filterable2, Filterable2C, Filterable3, Filterable3C } from './Filterable'
-import { identity } from './function'
+import { Option } from './Option'
+import { Either } from './Either'
 
 /**
  * `Separated` type which holds `left` and `right` parts
@@ -89,111 +86,3 @@ export interface Compactable3C<F extends URIS3, U, L> {
   readonly compact: <A>(fa: Type3<F, U, L, Option<A>>) => Type3<F, U, L, A>
   readonly separate: <A, B>(fa: Type3<F, U, L, Either<A, B>>) => Separated<Type3<F, U, L, A>, Type3<F, U, L, B>>
 }
-
-/**
- * Gets default implementation of {@link Compactable.compact} using {@link Compactable.separate}
- * @function
- * @since 1.7.0
- */
-export function getCompactFromSeparate<F extends URIS3, U, L>(
-  F: Functor3C<F, U, L> & Pick<Compactable3C<F, U, L>, 'separate'>
-): Compactable3C<F, U, L>['compact']
-export function getCompactFromSeparate<F extends URIS3>(
-  F: Functor3<F> & Pick<Compactable3<F>, 'separate'>
-): Compactable3<F>['compact']
-export function getCompactFromSeparate<F extends URIS2, L>(
-  F: Functor2C<F, L> & Pick<Compactable2C<F, L>, 'separate'>
-): Compactable2C<F, L>['compact']
-export function getCompactFromSeparate<F extends URIS2>(
-  F: Functor2<F> & Pick<Compactable2<F>, 'separate'>
-): Compactable2<F>['compact']
-export function getCompactFromSeparate<F extends URIS>(
-  F: Functor1<F> & Pick<Compactable1<F>, 'separate'>
-): Compactable1<F>['compact']
-export function getCompactFromSeparate<F>(F: Functor<F> & Pick<Compactable<F>, 'separate'>): Compactable<F>['compact']
-export function getCompactFromSeparate<F>(F: Functor<F> & Pick<Compactable<F>, 'separate'>): Compactable<F>['compact'] {
-  return foa => F.separate(F.map(foa, fromOption(null))).right
-}
-
-/**
- * Gets default implementation of {@link Compactable.compact} using {@link Filterable.filterMap}
- * @function
- * @since 1.7.0
- */
-export function getCompactFromFilterMap<F extends URIS3, U, L>(
-  F: Pick<Filterable3C<F, U, L>, 'URI' | 'filterMap'>
-): Compactable3C<F, U, L>['compact']
-export function getCompactFromFilterMap<F extends URIS3>(
-  F: Pick<Filterable3<F>, 'URI' | 'filterMap'>
-): Compactable3<F>['compact']
-export function getCompactFromFilterMap<F extends URIS2, L>(
-  F: Pick<Filterable2C<F, L>, 'URI' | 'filterMap'>
-): Compactable2C<F, L>['compact']
-export function getCompactFromFilterMap<F extends URIS2>(
-  F: Pick<Filterable2<F>, 'URI' | 'filterMap'>
-): Compactable2<F>['compact']
-export function getCompactFromFilterMap<F extends URIS>(
-  F: Pick<Filterable1<F>, 'URI' | 'filterMap'>
-): Compactable1<F>['compact']
-export function getCompactFromFilterMap<F>(F: Pick<Filterable<F>, 'URI' | 'filterMap'>): Compactable<F>['compact']
-export function getCompactFromFilterMap<F>(F: Pick<Filterable<F>, 'URI' | 'filterMap'>): Compactable<F>['compact'] {
-  return fa => F.filterMap(fa, identity)
-}
-
-/**
- * Gets default implementation of {@link Compactable.separate} using {@link Compactable.compact}
- * @function
- * @since 1.7.0
- */
-export function getSeparateFromCompact<F extends URIS3, U, L>(
-  F: Functor3C<F, U, L> & Pick<Compactable3C<F, U, L>, 'compact'>
-): Compactable3C<F, U, L>['separate']
-export function getSeparateFromCompact<F extends URIS3>(
-  F: Functor3<F> & Pick<Compactable3<F>, 'compact'>
-): Compactable3<F>['separate']
-export function getSeparateFromCompact<F extends URIS2, L>(
-  F: Functor2C<F, L> & Pick<Compactable2C<F, L>, 'compact'>
-): Compactable2C<F, L>['separate']
-export function getSeparateFromCompact<F extends URIS2>(
-  F: Functor2<F> & Pick<Compactable2<F>, 'compact'>
-): Compactable2<F>['separate']
-export function getSeparateFromCompact<F extends URIS>(
-  F: Functor1<F> & Pick<Compactable1<F>, 'compact'>
-): Compactable1<F>['separate']
-export function getSeparateFromCompact<F>(F: Functor<F> & Pick<Compactable<F>, 'compact'>): Compactable<F>['separate']
-export function getSeparateFromCompact<F>(F: Functor<F> & Pick<Compactable<F>, 'compact'>): Compactable<F>['separate'] {
-  return fe => {
-    const left = F.compact(F.map(fe, e => fromEither(e.swap())))
-    const right = F.compact(F.map(fe, fromEither))
-    return { left, right }
-  }
-}
-
-/**
- * Gets default implementation of {@link Compactable.separate} using {@link Filterable.partitionMap}
- * @function
- * @since 1.7.0
- */
-export function getSeparateFromPartitionMap<F extends URIS3, U, L>(
-  F: Pick<Filterable3C<F, U, L>, 'URI' | 'partitionMap'>
-): Compactable3C<F, U, L>['separate']
-export function getSeparateFromPartitionMap<F extends URIS3>(
-  F: Pick<Filterable3<F>, 'URI' | 'partitionMap'>
-): Compactable3<F>['separate']
-export function getSeparateFromPartitionMap<F extends URIS2, L>(
-  F: Pick<Filterable2C<F, L>, 'URI' | 'partitionMap'>
-): Compactable2C<F, L>['separate']
-export function getSeparateFromPartitionMap<F extends URIS2>(
-  F: Pick<Filterable2<F>, 'URI' | 'partitionMap'>
-): Compactable2<F>['separate']
-export function getSeparateFromPartitionMap<F extends URIS>(
-  F: Pick<Filterable1<F>, 'URI' | 'partitionMap'>
-): Compactable1<F>['separate']
-export function getSeparateFromPartitionMap<F>(
-  F: Pick<Filterable<F>, 'URI' | 'partitionMap'>
-): Compactable<F>['separate']
-export function getSeparateFromPartitionMap<F>(
-  F: Pick<Filterable<F>, 'URI' | 'partitionMap'>
-): Compactable<F>['separate'] {
-  return fa => F.partitionMap(fa, identity)
-}
diff --git a/src/Either.ts b/src/Either.ts
index c13a91a5..b40eb16b 100644
--- a/src/Either.ts
+++ b/src/Either.ts
@@ -13,6 +13,7 @@ import { Traversable2 } from './Traversable'
 import { Validation } from './Validation'
 import { Compactable2C, Separated } from './Compactable'
 import { Monoid } from './Monoid'
+import { Filterable2C } from './Filterable'
 
 declare module './HKT' {
   interface URI2HKT2<L, A> {
@@ -436,26 +437,33 @@ export const isRight = <L, A>(fa: Either<L, A>): fa is Right<L, A> => {
  * @since 1.7.0
  */
 export function getCompactable<L>(ML: Monoid<L>): Compactable2C<URI, L> {
-  const compact = <A>(fa: Either<L, Option<A>>): Either<L, A> =>
-    fa.fold(l => left(l), r => r.foldL(() => left(ML.empty), a => right(a)))
-  const separate = <A, B>(fa: Either<L, Either<A, B>>): Separated<Either<L, A>, Either<L, B>> =>
-    fa.fold(
-      x => ({
-        left: left<L, A>(x),
-        right: left<L, B>(x)
-      }),
-      e =>
-        e.fold(
-          l => ({
-            left: right<L, A>(l),
-            right: left<L, B>(ML.empty)
-          }),
-          r => ({
-            left: left<L, A>(ML.empty),
-            right: right<L, B>(r)
-          })
-        )
-    )
+  const compact = <A>(fa: Either<L, Option<A>>): Either<L, A> => {
+    if (fa.isLeft()) {
+      return fa as any
+    }
+    if (fa.value.isNone()) {
+      return left(ML.empty)
+    }
+    return right(fa.value.value)
+  }
+  const separate = <A, B>(fa: Either<L, Either<A, B>>): Separated<Either<L, A>, Either<L, B>> => {
+    if (fa.isLeft()) {
+      return {
+        left: fa as any,
+        right: fa as any
+      }
+    }
+    if (fa.value.isLeft()) {
+      return {
+        left: right(fa.value.value),
+        right: left(ML.empty)
+      }
+    }
+    return {
+      left: left(ML.empty),
+      right: right(fa.value.value)
+    }
+  }
 
   return {
     URI,
@@ -465,6 +473,74 @@ export function getCompactable<L>(ML: Monoid<L>): Compactable2C<URI, L> {
   }
 }
 
+/**
+ * Builds {@link Filterable} instance for {@link Either} gived {@link Monoid} for the left side
+ * @function
+ * @since 1.7.0
+ */
+export function getFilterable<L>(ML: Monoid<L>): Filterable2C<URI, L> {
+  const C = getCompactable(ML)
+  const partitionMap = <RL, RR, A>(
+    fa: Either<L, A>,
+    f: (a: A) => Either<RL, RR>
+  ): Separated<Either<L, RL>, Either<L, RR>> => {
+    if (fa.isLeft()) {
+      return {
+        left: fa as any,
+        right: fa as any
+      }
+    }
+    const e = f(fa.value)
+    if (e.isLeft()) {
+      return {
+        left: right(e.value),
+        right: left(ML.empty)
+      }
+    }
+    return {
+      left: left(ML.empty),
+      right: right(e.value)
+    }
+  }
+  const partition = <A>(fa: Either<L, A>, p: Predicate<A>): Separated<Either<L, A>, Either<L, A>> => {
+    if (fa.isLeft()) {
+      return {
+        left: fa,
+        right: fa
+      }
+    }
+    if (p(fa.value)) {
+      return {
+        left: left(ML.empty),
+        right: right(fa.value)
+      }
+    }
+    return {
+      left: right(fa.value),
+      right: left(ML.empty)
+    }
+  }
+  const filterMap = <A, B>(fa: Either<L, A>, f: (a: A) => Option<B>): Either<L, B> => {
+    if (fa.isLeft()) {
+      return fa as any
+    }
+    const optionB = f(fa.value)
+    if (optionB.isSome()) {
+      return right(optionB.value)
+    }
+    return left(ML.empty)
+  }
+  const filter = <A>(fa: Either<L, A>, p: Predicate<A>): Either<L, A> => fa.filterOrElse(p, ML.empty)
+  return {
+    ...C,
+    map,
+    partitionMap,
+    filterMap,
+    partition,
+    filter
+  }
+}
+
 /**
  * @instance
  * @since 1.0.0
diff --git a/src/Filterable.ts b/src/Filterable.ts
index 0a63fc77..10773a68 100644
--- a/src/Filterable.ts
+++ b/src/Filterable.ts
@@ -9,9 +9,9 @@ import {
   Separated
 } from './Compactable'
 import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
-import { Either, fromPredicate as fromPredicateEither } from './Either'
-import { identity, not, Predicate } from './function'
-import { Option, fromPredicate as fromPredicateOption } from './Option'
+import { Either } from './Either'
+import { Predicate } from './function'
+import { Option } from './Option'
 
 /**
  * @typeclass
@@ -116,223 +116,3 @@ export interface Filterable3C<F extends URIS3, U, L> extends Functor3C<F, U, L>,
   readonly filterMap: <A, B>(fa: Type3<F, U, L, A>, f: (a: A) => Option<B>) => Type3<F, U, L, B>
   readonly filter: <A>(fa: Type3<F, U, L, A>, p: Predicate<A>) => Type3<F, U, L, A>
 }
-
-/**
- * Gets default implementation of {@link Filterable.partitionMap} using {@link Compactable.separate}
- * @function
- * @since 1.7.0
- */
-export function getPartitionMapFromSeparate<F extends URIS3, U, L>(
-  F: Functor3C<F, U, L> & Pick<Compactable3C<F, U, L>, 'separate'>
-): Filterable3C<F, U, L>['partitionMap']
-export function getPartitionMapFromSeparate<F extends URIS3>(
-  F: Functor3<F> & Pick<Compactable3<F>, 'separate'>
-): Filterable3<F>['partitionMap']
-export function getPartitionMapFromSeparate<F extends URIS2, L>(
-  F: Functor2C<F, L> & Pick<Compactable2C<F, L>, 'separate'>
-): Filterable2C<F, L>['partitionMap']
-export function getPartitionMapFromSeparate<F extends URIS2>(
-  F: Functor2<F> & Pick<Compactable2<F>, 'separate'>
-): Filterable2<F>['partitionMap']
-export function getPartitionMapFromSeparate<F extends URIS>(
-  F: Functor1<F> & Pick<Compactable1<F>, 'separate'>
-): Filterable1<F>['partitionMap']
-export function getPartitionMapFromSeparate<F>(
-  F: Functor<F> & Pick<Compactable<F>, 'separate'>
-): Filterable<F>['partitionMap']
-export function getPartitionMapFromSeparate<F>(
-  F: Functor<F> & Pick<Compactable<F>, 'separate'>
-): Filterable<F>['partitionMap'] {
-  return (fa, f) => F.separate(F.map(fa, f))
-}
-
-/**
- * Gets default implementation of {@link Filterable.partition} using {@link Filterable.partitionMap}
- * @function
- * @since 1.7.0
- */
-export function getPartitionFromPartitionMap<F extends URIS3, U, L>(
-  F: Pick<Filterable3C<F, U, L>, 'URI' | 'partitionMap'>
-): Filterable3C<F, U, L>['partition']
-export function getPartitionFromPartitionMap<F extends URIS3>(
-  F: Pick<Filterable3<F>, 'URI' | 'partitionMap'>
-): Filterable3<F>['partition']
-export function getPartitionFromPartitionMap<F extends URIS2, L>(
-  F: Pick<Filterable2C<F, L>, 'URI' | 'partitionMap'>
-): Filterable2C<F, L>['partition']
-export function getPartitionFromPartitionMap<F extends URIS2>(
-  F: Pick<Filterable2<F>, 'URI' | 'partitionMap'>
-): Filterable2<F>['partition']
-export function getPartitionFromPartitionMap<F extends URIS>(
-  F: Pick<Filterable1<F>, 'URI' | 'partitionMap'>
-): Filterable1<F>['partition']
-export function getPartitionFromPartitionMap<F>(
-  F: Pick<Filterable<F>, 'URI' | 'partitionMap'>
-): Filterable<F>['partition']
-export function getPartitionFromPartitionMap<F>(
-  F: Pick<Filterable<F>, 'URI' | 'partitionMap'>
-): Filterable<F>['partition'] {
-  return (fa, p) => F.partitionMap(fa, fromPredicateEither(p, identity))
-}
-
-/**
- * Gets default implementation of {@link Filterable.partition} using {@link Filterable.filter}
- * @function
- * @since 1.7.0
- */
-export function getPartitionFromFilter<F extends URIS3, U, L>(
-  F: Pick<Filterable3C<F, U, L>, 'URI' | 'filter'>
-): Filterable3C<F, U, L>['partition']
-export function getPartitionFromFilter<F extends URIS3>(
-  F: Pick<Filterable3<F>, 'URI' | 'filter'>
-): Filterable3<F>['partition']
-export function getPartitionFromFilter<F extends URIS2, L>(
-  F: Pick<Filterable2C<F, L>, 'URI' | 'filter'>
-): Filterable2C<F, L>['partition']
-export function getPartitionFromFilter<F extends URIS2>(
-  F: Pick<Filterable2<F>, 'URI' | 'filter'>
-): Filterable2<F>['partition']
-export function getPartitionFromFilter<F extends URIS>(
-  F: Pick<Filterable1<F>, 'URI' | 'filter'>
-): Filterable1<F>['partition']
-export function getPartitionFromFilter<F>(F: Pick<Filterable<F>, 'URI' | 'filter'>): Filterable<F>['partition']
-export function getPartitionFromFilter<F>(F: Pick<Filterable<F>, 'URI' | 'filter'>): Filterable<F>['partition'] {
-  return (fa, p) => ({
-    left: F.filter(fa, not(p)),
-    right: F.filter(fa, p)
-  })
-}
-
-/**
- * Gets default implementation of {@link Filterable.partition} using {@Filterable.filterMap}
- * @function
- * @since 1.7.0
- */
-export function getPartitionFromFilterMap<F extends URIS3, U, L>(
-  F: Pick<Filterable3C<F, U, L>, 'URI' | 'filterMap'>
-): Filterable3C<F, U, L>['partition']
-export function getPartitionFromFilterMap<F extends URIS3>(
-  F: Pick<Filterable3<F>, 'URI' | 'filterMap'>
-): Filterable3<F>['partition']
-export function getPartitionFromFilterMap<F extends URIS2, L>(
-  F: Pick<Filterable2C<F, L>, 'URI' | 'filterMap'>
-): Filterable2C<F, L>['partition']
-export function getPartitionFromFilterMap<F extends URIS2>(
-  F: Pick<Filterable2<F>, 'URI' | 'filterMap'>
-): Filterable2<F>['partition']
-export function getPartitionFromFilterMap<F extends URIS>(
-  F: Pick<Filterable1<F>, 'URI' | 'filterMap'>
-): Filterable1<F>['partition']
-export function getPartitionFromFilterMap<F>(F: Pick<Filterable<F>, 'URI' | 'filterMap'>): Filterable<F>['partition']
-export function getPartitionFromFilterMap<F>(F: Pick<Filterable<F>, 'URI' | 'filterMap'>): Filterable<F>['partition'] {
-  return (fa, p) => ({
-    left: F.filterMap(fa, fromPredicateOption(not(p))),
-    right: F.filterMap(fa, fromPredicateOption(p))
-  })
-}
-
-/**
- * Gets default implementation of {@link Filterable.filterMap} using {@link Compactable.compact}
- * @function
- * @since 1.7.0
- */
-export function getFilterMapFromCompact<F extends URIS3, U, L>(
-  F: Functor3C<F, U, L> & Pick<Compactable3C<F, U, L>, 'compact'>
-): Filterable3C<F, U, L>['filterMap']
-export function getFilterMapFromCompact<F extends URIS3>(
-  F: Functor3<F> & Pick<Compactable3<F>, 'compact'>
-): Filterable3<F>['filterMap']
-export function getFilterMapFromCompact<F extends URIS2, L>(
-  F: Functor2C<F, L> & Pick<Compactable2C<F, L>, 'compact'>
-): Filterable2C<F, L>['filterMap']
-export function getFilterMapFromCompact<F extends URIS2>(
-  F: Functor2<F> & Pick<Compactable2<F>, 'compact'>
-): Filterable2<F>['filterMap']
-export function getFilterMapFromCompact<F extends URIS>(
-  F: Functor1<F> & Pick<Compactable1<F>, 'compact'>
-): Filterable1<F>['filterMap']
-export function getFilterMapFromCompact<F>(F: Functor<F> & Pick<Compactable<F>, 'compact'>): Filterable<F>['filterMap']
-export function getFilterMapFromCompact<F>(
-  F: Functor<F> & Pick<Compactable<F>, 'compact'>
-): Filterable<F>['filterMap'] {
-  return (fa, f) => F.compact(F.map(fa, f))
-}
-
-/**
- * Gets default implementation of {@link Filterable.filter} using {@link Filterable.filterMap}
- * @function
- * @since 1.7.0
- */
-export function getFilterFromFilterMap<F extends URIS3, U, L>(
-  F: Pick<Filterable3C<F, U, L>, 'URI' | 'filterMap'>
-): Filterable3C<F, U, L>['filter']
-export function getFilterFromFilterMap<F extends URIS3>(
-  F: Pick<Filterable3<F>, 'URI' | 'filterMap'>
-): Filterable3<F>['filter']
-export function getFilterFromFilterMap<F extends URIS2, L>(
-  F: Pick<Filterable2C<F, L>, 'URI' | 'filterMap'>
-): Filterable2C<F, L>['filter']
-export function getFilterFromFilterMap<F extends URIS2>(
-  F: Pick<Filterable2<F>, 'URI' | 'filterMap'>
-): Filterable2<F>['filter']
-export function getFilterFromFilterMap<F extends URIS>(
-  F: Pick<Filterable1<F>, 'URI' | 'filterMap'>
-): Filterable1<F>['filter']
-export function getFilterFromFilterMap<F>(F: Pick<Filterable<F>, 'URI' | 'filterMap'>): Filterable<F>['filter']
-export function getFilterFromFilterMap<F>(F: Pick<Filterable<F>, 'URI' | 'filterMap'>): Filterable<F>['filter'] {
-  return (fa, p) => F.filterMap(fa, fromPredicateOption(p))
-}
-
-/**
- * Gets default implementation of {@link Filterable.filter} using {@link Filterable.partition}
- * @function
- * @since 1.7.0
- */
-export function getFilterFromPartition<F extends URIS3, U, L>(
-  F: Functor3C<F, U, L> & Pick<Filterable3C<F, U, L>, 'URI' | 'partition'>
-): Filterable3C<F, U, L>['filter']
-export function getFilterFromPartition<F extends URIS3>(
-  F: Functor3<F> & Pick<Filterable3<F>, 'URI' | 'partition'>
-): Filterable3<F>['filter']
-export function getFilterFromPartition<F extends URIS2, L>(
-  F: Functor2C<F, L> & Pick<Filterable2C<F, L>, 'URI' | 'partition'>
-): Filterable2C<F, L>['filter']
-export function getFilterFromPartition<F extends URIS2>(
-  F: Functor2<F> & Pick<Filterable2<F>, 'URI' | 'partition'>
-): Filterable2<F>['filter']
-export function getFilterFromPartition<F extends URIS>(
-  F: Functor1<F> & Pick<Filterable1<F>, 'URI' | 'partition'>
-): Filterable1<F>['filter']
-export function getFilterFromPartition<F>(
-  F: Functor<F> & Pick<Filterable<F>, 'URI' | 'partition'>
-): Filterable<F>['filter']
-export function getFilterFromPartition<F>(
-  F: Functor<F> & Pick<Filterable<F>, 'URI' | 'partition'>
-): Filterable<F>['filter'] {
-  return (fa, p) => F.partition(fa, p).right
-}
-
-/**
- * Gets default implementation of {@link Filterable.filter} using {@link Filterable.partitionMap}
- * @function
- * @since 1.7.0
- */
-export function getFilterFromPartitionMap<F extends URIS3, U, L>(
-  F: Pick<Filterable3C<F, U, L>, 'URI' | 'partitionMap'>
-): Filterable3C<F, U, L>['filter']
-export function getFilterFromPartitionMap<F extends URIS3>(
-  F: Pick<Filterable3<F>, 'URI' | 'partitionMap'>
-): Filterable3<F>['filter']
-export function getFilterFromPartitionMap<F extends URIS2, L>(
-  F: Pick<Filterable2C<F, L>, 'URI' | 'partitionMap'>
-): Filterable2C<F, L>['filter']
-export function getFilterFromPartitionMap<F extends URIS2>(
-  F: Pick<Filterable2<F>, 'URI' | 'partitionMap'>
-): Filterable2<F>['filter']
-export function getFilterFromPartitionMap<F extends URIS>(
-  F: Pick<Filterable1<F>, 'URI' | 'partitionMap'>
-): Filterable1<F>['filter']
-export function getFilterFromPartitionMap<F>(F: Pick<Filterable<F>, 'URI' | 'partitionMap'>): Filterable<F>['filter']
-export function getFilterFromPartitionMap<F>(F: Pick<Filterable<F>, 'URI' | 'partitionMap'>): Filterable<F>['filter'] {
-  return (fa, p) => F.partitionMap(fa, fromPredicateEither(p, identity)).right
-}
diff --git a/src/Option.ts b/src/Option.ts
index 8794cb78..56b2dd6d 100644
--- a/src/Option.ts
+++ b/src/Option.ts
@@ -11,8 +11,9 @@ import { Plus1 } from './Plus'
 import { Semigroup } from './Semigroup'
 import { Setoid } from './Setoid'
 import { Traversable1 } from './Traversable'
-import { identity, Lazy, Predicate, Refinement, toString } from './function'
+import { identity, Lazy, not, Predicate, Refinement, toString } from './function'
 import { Compactable1, Separated } from './Compactable'
+import { Filterable1 } from './Filterable'
 
 declare module './HKT' {
   interface URI2HKT<A> {
@@ -555,26 +556,35 @@ export const isNone = <A>(fa: Option<A>): fa is None<A> => {
 export const fromRefinement = <A, B extends A>(refinement: Refinement<A, B>) => (a: A): Option<B> => {
   return refinement(a) ? some(a) : none
 }
-
 const compact = <A>(fa: Option<Option<A>>): Option<A> => fa.chain(identity)
-const separate = <RL, RR>(fa: Option<Either<RL, RR>>): Separated<Option<RL>, Option<RR>> =>
-  fa.foldL(
-    () => ({
+const separate = <RL, RR>(fa: Option<Either<RL, RR>>): Separated<Option<RL>, Option<RR>> => {
+  if (fa.isNone()) {
+    return {
       left: none,
       right: none
-    }),
-    e =>
-      e.fold<Separated<Option<RL>, Option<RR>>>(
-        l => ({
-          left: some(l),
-          right: none
-        }),
-        r => ({
-          left: none,
-          right: some(r)
-        })
-      )
-  )
+    }
+  }
+  const e = fa.value
+  if (e.isLeft()) {
+    return {
+      left: some(e.value),
+      right: none
+    }
+  }
+  return {
+    left: none,
+    right: some(e.value)
+  }
+}
+
+const filter = <A>(fa: Option<A>, p: Predicate<A>): Option<A> => fa.filter(p)
+const filterMap = chain
+const partitionMap = <RL, RR, A>(fa: Option<A>, f: (a: A) => Either<RL, RR>): Separated<Option<RL>, Option<RR>> =>
+  separate(fa.map(f))
+const partition = <A>(fa: Option<A>, p: Predicate<A>): Separated<Option<A>, Option<A>> => ({
+  left: fa.filter(not(p)),
+  right: fa.filter(p)
+})
 
 /**
  * @instance
@@ -586,7 +596,8 @@ export const option: Monad1<URI> &
   Traversable1<URI> &
   Alternative1<URI> &
   Extend1<URI> &
-  Compactable1<URI> = {
+  Compactable1<URI> &
+  Filterable1<URI> = {
   URI,
   map,
   of,
@@ -598,5 +609,9 @@ export const option: Monad1<URI> &
   alt,
   extend,
   compact,
-  separate
+  separate,
+  filter,
+  filterMap,
+  partition,
+  partitionMap
 }
diff --git a/src/StrMap.ts b/src/StrMap.ts
index dba2eb7d..ad5e7d1d 100644
--- a/src/StrMap.ts
+++ b/src/StrMap.ts
@@ -12,6 +12,7 @@ import { Unfoldable } from './Unfoldable'
 import { Predicate, tuple } from './function'
 import { Either } from './Either'
 import { Compactable1, Separated } from './Compactable'
+import { Filterable1 } from './Filterable'
 
 // https://github.com/purescript/purescript-maps
 
@@ -59,15 +60,7 @@ export class StrMap<A> {
    * @since 1.4.0
    */
   filter(p: Predicate<A>): StrMap<A> {
-    const o = this.value
-    const r: { [key: string]: A } = {}
-    for (const k in o) {
-      const value = o[k]
-      if (p(value)) {
-        r[k] = value
-      }
-    }
-    return new StrMap(r)
+    return filter(this, p)
   }
 }
 
@@ -295,32 +288,94 @@ export const pop = <A>(k: string, d: StrMap<A>): Option<[A, StrMap<A>]> => {
   return a.isNone() ? none : some(tuple(a.value, remove(k, d)))
 }
 
-const compact = <A>(fa: StrMap<Option<A>>): StrMap<A> => {
+const filterMap = <A, B>(fa: StrMap<A>, f: (a: A) => Option<B>): StrMap<B> => {
+  const value = fa.value
+  const result: { [key: string]: B } = {}
+  const keys = Object.keys(value)
+  for (const key of keys) {
+    const optionB = f(value[key])
+    if (optionB.isSome()) {
+      result[key] = optionB.value
+    }
+  }
+  return new StrMap(result)
+}
+
+const filter = <A>(fa: StrMap<A>, p: Predicate<A>): StrMap<A> => {
+  const value = fa.value
   const result: { [key: string]: A } = {}
+  const keys = Object.keys(value)
+  for (const key of keys) {
+    const a = value[key]
+    if (p(a)) {
+      result[key] = a
+    }
+  }
+  return new StrMap(result)
+}
 
+const compact = <A>(fa: StrMap<Option<A>>): StrMap<A> => {
   const value = fa.value
+  const result: { [key: string]: A } = {}
   const keys = Object.keys(value)
-  for (let key of keys) {
+  for (const key of keys) {
     const optionA = value[key]
     if (optionA.isSome()) {
       result[key] = optionA.value
     }
   }
-
   return new StrMap(result)
 }
 
-const separate = <RL, RR>(fa: StrMap<Either<RL, RR>>): Separated<StrMap<RL>, StrMap<RR>> => {
+const partitionMap = <RL, RR, A>(fa: StrMap<A>, f: (a: A) => Either<RL, RR>): Separated<StrMap<RL>, StrMap<RR>> => {
+  const value = fa.value
   const left: { [key: string]: RL } = {}
   const right: { [key: string]: RR } = {}
+  const keys = Object.keys(value)
+  for (const key of keys) {
+    const e = f(value[key])
+    if (e.isLeft()) {
+      left[key] = e.value
+    } else {
+      right[key] = e.value
+    }
+  }
+  return {
+    left: new StrMap(left),
+    right: new StrMap(right)
+  }
+}
+
+const partition = <A>(fa: StrMap<A>, p: Predicate<A>): Separated<StrMap<A>, StrMap<A>> => {
   const value = fa.value
+  const left: { [key: string]: A } = {}
+  const right: { [key: string]: A } = {}
+  const keys = Object.keys(value)
+  for (const key of keys) {
+    const a = value[key]
+    if (p(a)) {
+      right[key] = a
+    } else {
+      left[key] = a
+    }
+  }
+  return {
+    left: new StrMap(left),
+    right: new StrMap(right)
+  }
+}
+
+const separate = <RL, RR>(fa: StrMap<Either<RL, RR>>): Separated<StrMap<RL>, StrMap<RR>> => {
+  const value = fa.value
+  const left: { [key: string]: RL } = {}
+  const right: { [key: string]: RR } = {}
   const keys = Object.keys(value)
-  for (let key of keys) {
-    const eitherA = value[key]
-    if (eitherA.isLeft()) {
-      left[key] = eitherA.value
+  for (const key of keys) {
+    const e = value[key]
+    if (e.isLeft()) {
+      left[key] = e.value
     } else {
-      right[key] = eitherA.value
+      right[key] = e.value
     }
   }
   return {
@@ -333,11 +388,15 @@ const separate = <RL, RR>(fa: StrMap<Either<RL, RR>>): Separated<StrMap<RL>, Str
  * @instance
  * @since 1.0.0
  */
-export const strmap: Functor1<URI> & Foldable1<URI> & Traversable1<URI> & Compactable1<URI> = {
+export const strmap: Functor1<URI> & Foldable1<URI> & Traversable1<URI> & Compactable1<URI> & Filterable1<URI> = {
   URI,
   map,
   reduce,
   traverse,
   compact,
-  separate
+  separate,
+  filter,
+  filterMap,
+  partition,
+  partitionMap
 }
diff --git a/src/Validation.ts b/src/Validation.ts
index ead7be89..cf0c3684 100644
--- a/src/Validation.ts
+++ b/src/Validation.ts
@@ -13,6 +13,7 @@ import { Predicate, phantom, toString } from './function'
 import { Bifunctor2 } from './Bifunctor'
 import { Compactable2C, Separated } from './Compactable'
 import { Option } from './Option'
+import { Filterable2C } from './Filterable'
 
 // Adapted from https://github.com/purescript/purescript-validation
 
@@ -331,28 +332,34 @@ export const isSuccess = <L, A>(fa: Validation<L, A>): fa is Success<L, A> => {
  * @since 1.7.0
  */
 export function getCompactable<L>(ML: Monoid<L>): Compactable2C<URI, L> {
-  const compact = <A>(fa: Validation<L, Option<A>>): Validation<L, A> =>
-    fa.fold(l => failure(l), optionA => optionA.foldL(() => failure(ML.empty), a => success(a)))
-
-  const separate = <RL, RR, A>(fa: Validation<L, Either<RL, RR>>): Separated<Validation<L, RL>, Validation<L, RR>> =>
-    fa.fold(
-      l => ({
-        left: failure<L, RL>(l),
-        right: failure<L, RR>(l)
-      }),
-      e =>
-        e.fold(
-          l => ({
-            left: success<L, RL>(l),
-            right: failure<L, RR>(ML.empty)
-          }),
-          r => ({
-            left: failure<L, RL>(ML.empty),
-            right: success<L, RR>(r)
-          })
-        )
-    )
+  const compact = <A>(fa: Validation<L, Option<A>>): Validation<L, A> => {
+    if (fa.isFailure()) {
+      return fa as any
+    }
+    if (fa.value.isNone()) {
+      return failure(ML.empty)
+    }
+    return success(fa.value.value)
+  }
 
+  const separate = <RL, RR, A>(fa: Validation<L, Either<RL, RR>>): Separated<Validation<L, RL>, Validation<L, RR>> => {
+    if (fa.isFailure()) {
+      return {
+        left: fa as any,
+        right: fa as any
+      }
+    }
+    if (fa.value.isLeft()) {
+      return {
+        left: success(fa.value.value),
+        right: failure(ML.empty)
+      }
+    }
+    return {
+      left: failure(ML.empty),
+      right: success(fa.value.value)
+    }
+  }
   return {
     URI,
     _L: phantom,
@@ -361,6 +368,83 @@ export function getCompactable<L>(ML: Monoid<L>): Compactable2C<URI, L> {
   }
 }
 
+/**
+ * Builds {@link Filterable} instance for {@link Validation} gived {@link Monoid} for the left side
+ * @function
+ * @since 1.7.0
+ */
+export function getFilterable<L>(ML: Monoid<L>): Filterable2C<URI, L> {
+  const C = getCompactable(ML)
+  const partitionMap = <RL, RR, A>(
+    fa: Validation<L, A>,
+    f: (a: A) => Either<RL, RR>
+  ): Separated<Validation<L, RL>, Validation<L, RR>> => {
+    if (fa.isFailure()) {
+      return {
+        left: fa as any,
+        right: fa as any
+      }
+    }
+    const e = f(fa.value)
+    if (e.isLeft()) {
+      return {
+        left: success(e.value),
+        right: failure(ML.empty)
+      }
+    }
+    return {
+      left: failure(ML.empty),
+      right: success(e.value)
+    }
+  }
+  const partition = <A>(fa: Validation<L, A>, p: Predicate<A>): Separated<Validation<L, A>, Validation<L, A>> => {
+    if (fa.isFailure()) {
+      return {
+        left: fa,
+        right: fa
+      }
+    }
+    if (p(fa.value)) {
+      return {
+        left: failure(ML.empty),
+        right: success(fa.value)
+      }
+    }
+    return {
+      left: success(fa.value),
+      right: failure(ML.empty)
+    }
+  }
+  const filterMap = <A, B>(fa: Validation<L, A>, f: (a: A) => Option<B>): Validation<L, B> => {
+    if (fa.isFailure()) {
+      return fa as any
+    }
+    const optionB = f(fa.value)
+    if (optionB.isSome()) {
+      return success(optionB.value)
+    }
+    return failure(ML.empty)
+  }
+  const filter = <A>(fa: Validation<L, A>, p: Predicate<A>): Validation<L, A> => {
+    if (fa.isFailure()) {
+      return fa
+    }
+    const a = fa.value
+    if (p(a)) {
+      return success(a)
+    }
+    return failure(ML.empty)
+  }
+  return {
+    ...C,
+    map,
+    partitionMap,
+    filterMap,
+    partition,
+    filter
+  }
+}
+
 /**
  * @instance
  * @since 1.0.0


Test patch:
diff --git a/test/Array.ts b/test/Array.ts
index dd14a8c6..17ee9a9f 100644
--- a/test/Array.ts
+++ b/test/Array.ts
@@ -225,18 +225,6 @@ describe('Array', () => {
     assert.deepEqual(modifyAt(as, 1, double), some([1, 4, 3]))
   })
 
-  it('mapOption', () => {
-    const f = (a: number) => (a % 2 === 0 ? none : some(a))
-    assert.deepEqual(mapOption([], f), [])
-    assert.deepEqual(mapOption(as, f), [1, 3])
-  })
-
-  it('catOptions', () => {
-    assert.deepEqual(catOptions([]), [])
-    assert.deepEqual(catOptions([some(1), some(2), some(3)]), [1, 2, 3])
-    assert.deepEqual(catOptions([some(1), none, some(3)]), [1, 3])
-  })
-
   it('sort', () => {
     assert.deepEqual(sort(ordNumber)([3, 2, 1]), [1, 2, 3])
   })
@@ -272,11 +260,6 @@ describe('Array', () => {
     assert.deepEqual(flatten([[1], [2], [3]]), [1, 2, 3])
   })
 
-  it('partitionMap', () => {
-    assert.deepEqual(partitionMap([], x => x), { left: [], right: [] })
-    assert.deepEqual(partitionMap([right(1), left('foo'), right(2)], x => x), { left: ['foo'], right: [1, 2] })
-  })
-
   it('rotate', () => {
     assert.deepEqual(rotate(1, []), [])
     assert.deepEqual(rotate(1, [1]), [1])
@@ -289,10 +272,6 @@ describe('Array', () => {
     assert.deepEqual(rotate(-2, [1, 2, 3, 4, 5]), [3, 4, 5, 1, 2])
   })
 
-  it('filter', () => {
-    assert.deepEqual(filter([1, 2, 3], n => n % 2 === 1), [1, 3])
-  })
-
   it('map', () => {
     assert.deepEqual(array.map([1, 2, 3], n => n * 2), [2, 4, 6])
   })
@@ -436,14 +415,43 @@ describe('Array', () => {
     ])
   })
 
-  it('compact', () => {
+  it('compact/catOptions', () => {
     assert.deepEqual(array.compact([]), [])
     assert.deepEqual(array.compact([some(1), some(2), some(3)]), [1, 2, 3])
     assert.deepEqual(array.compact([some(1), none, some(3)]), [1, 3])
+    assert.deepEqual(catOptions([]), [])
+    assert.deepEqual(catOptions([some(1), some(2), some(3)]), [1, 2, 3])
+    assert.deepEqual(catOptions([some(1), none, some(3)]), [1, 3])
   })
 
   it('separate', () => {
     assert.deepEqual(array.separate([]), { left: [], right: [] })
     assert.deepEqual(array.separate([left(123), right('123')]), { left: [123], right: ['123'] })
   })
+
+  it('filter', () => {
+    assert.deepEqual(filter([1, 2, 3], n => n % 2 === 1), [1, 3])
+    assert.deepEqual(array.filter([1, 2, 3], n => n % 2 === 1), [1, 3])
+  })
+
+  it('filterMap/mapOption', () => {
+    const f = (a: number) => (a % 2 === 0 ? none : some(a))
+    assert.deepEqual(mapOption([], f), [])
+    assert.deepEqual(mapOption(as, f), [1, 3])
+    assert.deepEqual(array.filterMap([], f), [])
+    assert.deepEqual(array.filterMap(as, f), [1, 3])
+  })
+
+  it('partitionMap', () => {
+    assert.deepEqual(partitionMap([], x => x), { left: [], right: [] })
+    assert.deepEqual(partitionMap([right(1), left('foo'), right(2)], x => x), { left: ['foo'], right: [1, 2] })
+    assert.deepEqual(array.partitionMap([], x => x), { left: [], right: [] })
+    assert.deepEqual(array.partitionMap([right(1), left('foo'), right(2)], x => x), { left: ['foo'], right: [1, 2] })
+  })
+
+  it('partition', () => {
+    const p = (n: number) => n > 2
+    assert.deepEqual(array.partition([], p), { left: [], right: [] })
+    assert.deepEqual(array.partition([1, 3], p), { left: [1], right: [3] })
+  })
 })
diff --git a/test/Compactable.ts b/test/Compactable.ts
deleted file mode 100644
index a9e32e82..00000000
--- a/test/Compactable.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import * as assert from 'assert'
-import { left, right } from '../src/Either'
-import { none, some } from '../src/Option'
-import {
-  getCompactFromFilterMap,
-  getCompactFromSeparate,
-  getSeparateFromCompact,
-  getSeparateFromPartitionMap
-} from '../src/Compactable'
-import { array, mapOption, partitionMap } from '../src/Array'
-
-describe('Compactable', () => {
-  it('getCompactFromSeparate', () => {
-    const { URI, map, separate } = array
-    const compactF = getCompactFromSeparate({
-      URI,
-      map,
-      separate
-    })
-    assert.deepEqual(compactF([]), [])
-    assert.deepEqual(compactF([none]), [])
-    assert.deepEqual(compactF([none, some(1)]), [1])
-  })
-
-  it('getCompactFromFilterMap', () => {
-    const compactF = getCompactFromFilterMap({
-      URI: array.URI,
-      filterMap: mapOption
-    })
-    assert.deepEqual(compactF([]), [])
-    assert.deepEqual(compactF([none]), [])
-    assert.deepEqual(compactF([none, some(1)]), [1])
-  })
-
-  it('getSeparateFromCompact', () => {
-    const { URI, map, compact } = array
-    const separateF = getSeparateFromCompact({
-      URI,
-      map,
-      compact
-    })
-    assert.deepEqual(separateF([]), { left: [], right: [] })
-    assert.deepEqual(separateF([left(1)]), { left: [1], right: [] })
-    assert.deepEqual(separateF([left(1), right(3)]), { left: [1], right: [3] })
-  })
-
-  it('getSeparateFromPartitionMap', () => {
-    const separateF = getSeparateFromPartitionMap({
-      URI: array.URI,
-      partitionMap
-    })
-
-    assert.deepEqual(separateF([]), { left: [], right: [] })
-    assert.deepEqual(separateF([left(1)]), { left: [1], right: [] })
-    assert.deepEqual(separateF([left(1), right(3)]), { left: [1], right: [3] })
-  })
-})
diff --git a/test/Either.ts b/test/Either.ts
index 8f32174f..fafe9b6f 100644
--- a/test/Either.ts
+++ b/test/Either.ts
@@ -14,7 +14,8 @@ import {
   isLeft,
   isRight,
   fromRefinement,
-  getCompactable
+  getCompactable,
+  getFilterable
 } from '../src/Either'
 import { none, option, some } from '../src/Option'
 import { setoidNumber, setoidString } from '../src/Setoid'
@@ -287,4 +288,37 @@ describe('Either', () => {
       assert.deepEqual(C.separate(right(right('123'))), { left: left(monoidString.empty), right: right('123') })
     })
   })
+
+  describe('getFilterable', () => {
+    const F = getFilterable(monoidString)
+    const p = (n: number) => n > 2
+    it('partition', () => {
+      assert.deepEqual(F.partition(left<string, number>('123'), p), {
+        left: left('123'),
+        right: left('123')
+      })
+      assert.deepEqual(F.partition(right<string, number>(1), p), { left: right(1), right: left(monoidString.empty) })
+      assert.deepEqual(F.partition(right<string, number>(3), p), { left: left(monoidString.empty), right: right(3) })
+    })
+    it('partitionMap', () => {
+      const f = (n: number) => (p(n) ? right(n + 1) : left(n - 1))
+      assert.deepEqual(F.partitionMap(left<string, number>('123'), f), {
+        left: left('123'),
+        right: left('123')
+      })
+      assert.deepEqual(F.partitionMap(right<string, number>(1), f), { left: right(0), right: left(monoidString.empty) })
+      assert.deepEqual(F.partitionMap(right<string, number>(3), f), { left: left(monoidString.empty), right: right(4) })
+    })
+    it('filter', () => {
+      assert.deepEqual(F.filter(left<string, number>('123'), p), left('123'))
+      assert.deepEqual(F.filter(right<string, number>(1), p), left(monoidString.empty))
+      assert.deepEqual(F.filter(right<string, number>(3), p), right(3))
+    })
+    it('filterMap', () => {
+      const f = (n: number) => (p(n) ? some(n + 1) : none)
+      assert.deepEqual(F.filterMap(left<string, number>('123'), f), left('123'))
+      assert.deepEqual(F.filterMap(right<string, number>(1), f), left(monoidString.empty))
+      assert.deepEqual(F.filterMap(right<string, number>(3), f), right(4))
+    })
+  })
 })
diff --git a/test/Filterable.ts b/test/Filterable.ts
deleted file mode 100644
index f38c5811..00000000
--- a/test/Filterable.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-import * as assert from 'assert'
-import {
-  getFilterFromFilterMap,
-  getFilterFromPartition,
-  getFilterFromPartitionMap,
-  getFilterMapFromCompact,
-  getPartitionFromFilter,
-  getPartitionFromFilterMap,
-  getPartitionFromPartitionMap,
-  getPartitionMapFromSeparate
-} from '../src/Filterable'
-import { array, filter } from '../src/Array'
-import { left, right } from '../src/Either'
-import { none, some } from '../src/Option'
-
-describe('Filterable', () => {
-  const p = (n: number) => n > 2
-
-  it('getPartitionMapFromSeparate', () => {
-    const { URI, map, separate } = array
-    const F = {
-      URI,
-      map,
-      separate
-    }
-    const partitionMapF = getPartitionMapFromSeparate(F)
-    const f = (n: number) => (p(n) ? right(n + 1) : left(n - 1))
-    assert.deepEqual(partitionMapF([], f), { left: [], right: [] })
-    assert.deepEqual(partitionMapF([1, 3], f), { left: [0], right: [4] })
-  })
-
-  it('getPartitionFromPartitionMap', () => {
-    const { URI, map, separate } = array
-
-    const partitionMapF = getPartitionMapFromSeparate({
-      URI,
-      map,
-      separate
-    })
-
-    const partitionF = getPartitionFromPartitionMap({
-      URI,
-      partitionMap: partitionMapF
-    })
-
-    assert.deepEqual(partitionF([], p), { left: [], right: [] })
-    assert.deepEqual(partitionF([1, 3], p), { left: [1], right: [3] })
-  })
-
-  it('getPartitionFromFilter', () => {
-    const { URI } = array
-    const F = {
-      URI,
-      filter
-    }
-    const partitionF = getPartitionFromFilter(F)
-    assert.deepEqual(partitionF([], p), { left: [], right: [] })
-    assert.deepEqual(partitionF([1, 3], p), { left: [1], right: [3] })
-  })
-
-  it('getPartitionFromFilterMap', () => {
-    const { URI, map, compact } = array
-    const filterMapF = getFilterMapFromCompact({
-      URI,
-      map,
-      compact
-    })
-    const partitionF = getPartitionFromFilterMap({
-      URI,
-      filterMap: filterMapF
-    })
-
-    assert.deepEqual(partitionF([], p), { left: [], right: [] })
-    assert.deepEqual(partitionF([1, 3], p), { left: [1], right: [3] })
-  })
-
-  it('getFilterMapFromCompact', () => {
-    const { URI, map, compact } = array
-    const F = {
-      URI,
-      map,
-      compact
-    }
-    const filterMapF = getFilterMapFromCompact(F)
-    const f = (n: number) => (p(n) ? some(n + 1) : none)
-    assert.deepEqual(filterMapF([], f), [])
-    assert.deepEqual(filterMapF([1, 3], f), [4])
-  })
-
-  it('getFilterFromFilterMap', () => {
-    const { URI, map, compact } = array
-    const filterMapF = getFilterMapFromCompact({
-      URI,
-      map,
-      compact
-    })
-    const filterF = getFilterFromFilterMap({
-      URI,
-      filterMap: filterMapF
-    })
-
-    assert.deepEqual(filterF([], p), [])
-    assert.deepEqual(filterF([1, 3], p), [3])
-  })
-
-  it('getFilterFromPartition', () => {
-    const { URI, map } = array
-    const partition = getPartitionFromFilter({ URI, filter })
-    const filterF = getFilterFromPartition({ URI, map, partition })
-    assert.deepEqual(filterF([], p), [])
-    assert.deepEqual(filterF([1, 3], p), [3])
-  })
-
-  it('getFilterFromPartitionMap', () => {
-    const { URI, map, separate } = array
-
-    const partitionMapF = getPartitionMapFromSeparate({
-      URI,
-      map,
-      separate
-    })
-
-    const filterF = getFilterFromPartitionMap({
-      URI,
-      partitionMap: partitionMapF
-    })
-
-    const p = (n: number) => n > 2
-
-    assert.deepEqual(filterF([], p), [])
-    assert.deepEqual(filterF([1, 3], p), [3])
-  })
-})
diff --git a/test/Option.ts b/test/Option.ts
index 3965fd64..a2e4bd23 100644
--- a/test/Option.ts
+++ b/test/Option.ts
@@ -244,15 +244,6 @@ describe('Option', () => {
     assert.equal(some(2).exists(is2), true)
   })
 
-  it('filter', () => {
-    const x: Option<number> = none
-    const is2 = (a: number) => a === 2
-    assert.equal(x.filter(is2), x)
-    assert.equal(some(1).filter(is2), none)
-    const some2 = some(2)
-    assert.equal(some2.filter(is2), some2)
-  })
-
   it('refine', () => {
     const x: Option<number | string> = none
     const isString = (a: any): a is string => typeof a === 'string'
@@ -299,4 +290,36 @@ describe('Option', () => {
     assert.deepEqual(option.separate(some(left('123'))), { left: some('123'), right: none })
     assert.deepEqual(option.separate(some(right('123'))), { left: none, right: some('123') })
   })
+
+  it('filter', () => {
+    const x: Option<number> = none
+    const is2 = (a: number) => a === 2
+    assert.equal(x.filter(is2), x)
+    assert.equal(option.filter(x, is2), x)
+    assert.equal(some(1).filter(is2), none)
+    assert.equal(option.filter(some(1), is2), none)
+    const some2 = some(2)
+    assert.equal(some2.filter(is2), some2)
+    assert.equal(option.filter(some2, is2), some2)
+  })
+
+  it('filterMap', () => {
+    const f = (n: number) => (p(n) ? some(n + 1) : none)
+    assert.deepEqual(option.filterMap(none, f), none)
+    assert.deepEqual(option.filterMap(some(1), f), none)
+    assert.deepEqual(option.filterMap(some(3), f), some(4))
+  })
+
+  it('partition', () => {
+    assert.deepEqual(option.partition(none, p), { left: none, right: none })
+    assert.deepEqual(option.partition(some(1), p), { left: some(1), right: none })
+    assert.deepEqual(option.partition(some(3), p), { left: none, right: some(3) })
+  })
+
+  it('partitionMap', () => {
+    const f = (n: number) => (p(n) ? right(n + 1) : left(n - 1))
+    assert.deepEqual(option.partitionMap(none, f), { left: none, right: none })
+    assert.deepEqual(option.partitionMap(some(1), f), { left: some(0), right: none })
+    assert.deepEqual(option.partitionMap(some(3), f), { left: none, right: some(4) })
+  })
 })
diff --git a/test/StrMap.ts b/test/StrMap.ts
index 3f75b193..0c862968 100644
--- a/test/StrMap.ts
+++ b/test/StrMap.ts
@@ -22,6 +22,8 @@ import { traverse } from '../src/Traversable'
 import { semigroupSum } from '../src/Semigroup'
 import { left, right } from '../src/Either'
 
+const p = (n: number) => n > 2
+
 describe('StrMap', () => {
   it('getMonoid', () => {
     const d1 = new StrMap<number>({ k1: 1, k2: 3 })
@@ -128,11 +130,6 @@ describe('StrMap', () => {
     assert.deepEqual(insert('c', 3, new StrMap({ a: 1, b: 2 })), new StrMap({ a: 1, b: 2, c: 3 }))
   })
 
-  it('filter', () => {
-    const d = new StrMap({ a: 1, b: 2 })
-    assert.deepEqual(d.filter(a => a === 1), new StrMap({ a: 1 }))
-  })
-
   it('compact', () => {
     assert.deepEqual(strmap.compact(new StrMap({ foo: none, bar: some(123) })), new StrMap({ bar: 123 }))
   })
@@ -143,4 +140,33 @@ describe('StrMap', () => {
       right: new StrMap({ bar: 123 })
     })
   })
+
+  it('filter', () => {
+    const d = new StrMap({ a: 1, b: 3 })
+    assert.deepEqual(d.filter(p), new StrMap({ b: 3 }))
+    assert.deepEqual(strmap.filter(d, p), new StrMap({ b: 3 }))
+  })
+
+  it('filterMap', () => {
+    const f = (n: number) => (p(n) ? some(n + 1) : none)
+    assert.deepEqual(strmap.filterMap(new StrMap<number>({}), f), new StrMap({}))
+    assert.deepEqual(strmap.filterMap(new StrMap({ a: 1, b: 3 }), f), new StrMap({ b: 4 }))
+  })
+
+  it('partition', () => {
+    assert.deepEqual(strmap.partition(new StrMap<number>({}), p), { left: new StrMap({}), right: new StrMap({}) })
+    assert.deepEqual(strmap.partition(new StrMap<number>({ a: 1, b: 3 }), p), {
+      left: new StrMap({ a: 1 }),
+      right: new StrMap({ b: 3 })
+    })
+  })
+
+  it('partitionMap', () => {
+    const f = (n: number) => (p(n) ? right(n + 1) : left(n - 1))
+    assert.deepEqual(strmap.partitionMap(new StrMap<number>({}), f), { left: new StrMap({}), right: new StrMap({}) })
+    assert.deepEqual(strmap.partitionMap(new StrMap<number>({ a: 1, b: 3 }), f), {
+      left: new StrMap({ a: 0 }),
+      right: new StrMap({ b: 4 })
+    })
+  })
 })
diff --git a/test/Validation.ts b/test/Validation.ts
index 7d09f5de..05090e68 100644
--- a/test/Validation.ts
+++ b/test/Validation.ts
@@ -19,10 +19,13 @@ import {
   success,
   validation,
   getMonoid,
-  getCompactable
+  getCompactable,
+  getFilterable
 } from '../src/Validation'
 import { left, right } from '../src/Either'
 
+const p = (n: number): boolean => n > 2
+
 describe('Validation', () => {
   it('getMonad', () => {
     const M = getMonad(monoidString)
@@ -148,10 +151,9 @@ describe('Validation', () => {
 
   it('bimap', () => {
     const f = (s: string): number => s.length
-    const g = (n: number): boolean => n > 2
-    assert.deepEqual(success<string, number>(1).bimap(f, g), success(false))
-    assert.deepEqual(failure<string, number>('foo').bimap(f, g), failure(3))
-    assert.deepEqual(validation.bimap(success<string, number>(1), f, g), success(false))
+    assert.deepEqual(success<string, number>(1).bimap(f, p), success(false))
+    assert.deepEqual(failure<string, number>('foo').bimap(f, p), failure(3))
+    assert.deepEqual(validation.bimap(success<string, number>(1), f, p), success(false))
   })
 
   it('fromPredicate', () => {
@@ -190,4 +192,48 @@ describe('Validation', () => {
       assert.deepEqual(C.separate(success(right('123'))), { left: failure(monoidString.empty), right: success('123') })
     })
   })
+
+  describe('getFilterable', () => {
+    const F = getFilterable(monoidString)
+    it('partition', () => {
+      assert.deepEqual(F.partition(failure<string, number>('123'), p), {
+        left: failure('123'),
+        right: failure('123')
+      })
+      assert.deepEqual(F.partition(success<string, number>(1), p), {
+        left: success(1),
+        right: failure(monoidString.empty)
+      })
+      assert.deepEqual(F.partition(success<string, number>(3), p), {
+        left: failure(monoidString.empty),
+        right: success(3)
+      })
+    })
+    it('partitionMap', () => {
+      const f = (n: number) => (p(n) ? right(n + 1) : left(n - 1))
+      assert.deepEqual(F.partitionMap(failure<string, number>('123'), f), {
+        left: failure('123'),
+        right: failure('123')
+      })
+      assert.deepEqual(F.partitionMap(success<string, number>(1), f), {
+        left: success(0),
+        right: failure(monoidString.empty)
+      })
+      assert.deepEqual(F.partitionMap(success<string, number>(3), f), {
+        left: failure(monoidString.empty),
+        right: success(4)
+      })
+    })
+    it('filter', () => {
+      assert.deepEqual(F.filter(failure<string, number>('123'), p), failure('123'))
+      assert.deepEqual(F.filter(success<string, number>(1), p), failure(monoidString.empty))
+      assert.deepEqual(F.filter(success<string, number>(3), p), success(3))
+    })
+    it('filterMap', () => {
+      const f = (n: number) => (p(n) ? some(n + 1) : none)
+      assert.deepEqual(F.filterMap(failure<string, number>('123'), f), failure('123'))
+      assert.deepEqual(F.filterMap(success<string, number>(1), f), failure(monoidString.empty))
+      assert.deepEqual(F.filterMap(success<string, number>(3), f), success(4))
+    })
+  })
 })