Repository: gcanti/fp-ts
Pull request #492 (2018-06-22 13:12:16)

Feature/filterable
Part of https://github.com/gcanti/fp-ts/pull/485

Adds:
  -  `Filterable` typeclass
  - default implementations for `Filterable` methods
  - default implementations for `Compactable` methods via `Filterable`
  - `eitherBool`, `optionBool` helpers
  - tests
  - `.prettierrc` and `.prettierignore` to support WebStorm watchers

Changes:
  - `Compactable` helper names
  - removes `CompactableA` and `Compactable1A` which were accidentaly added in https://github.com/gcanti/fp-ts/pull/488


Patch:
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..126d17fe
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,6 @@
+/lib
+/docs
+/docs-generator
+/perf
+/node_modules
+/typings-checker
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..a5187b8f
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,6 @@
+{
+  "semi": false,
+  "singleQuote": true,
+  "printWidth": 120,
+  "parser": "typescript"
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 0e023e05..8ce38e4b 100644
--- a/package.json
+++ b/package.json
@@ -11,8 +11,8 @@
     "lint": "tslint -p tsconfig.json src/**/*.ts test/**/*.ts examples/**/*.ts exercises/**/*.ts fantas-eel-and-specification/**/*.ts",
     "jest": "jest --ci",
     "jest-coverage": "jest --ci --coverage",
-    "prettier": "prettier --no-semi --single-quote --print-width 120 --parser typescript --list-different \"{src,test,examples,exercises}/**/*.ts\"",
-    "fix-prettier": "prettier --no-semi --single-quote --print-width 120 --parser typescript --write \"{src,test,examples,exercises}/**/*.ts\"",
+    "prettier": "prettier --list-different \"./{src,test,examples,exercises}/**/*.ts\"",
+    "fix-prettier": "prettier --write \"./{src,test,examples,exercises}/**/*.ts\"",
     "typings": "typings-checker --project ./typings-checker/tsconfig.json ./typings-checker/index.ts",
     "test": "npm run lint && npm run prettier && npm run typings && npm run jest",
     "clean": "rimraf lib/*",
diff --git a/src/Compactable.ts b/src/Compactable.ts
index c1c8fe5b..1fb0baf2 100644
--- a/src/Compactable.ts
+++ b/src/Compactable.ts
@@ -2,6 +2,8 @@ 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'
 
 /**
  * `Separated` type which holds `left` and `right` parts
@@ -35,19 +37,6 @@ export interface Compactable<F> {
   readonly separate: <A, B>(fa: HKT<F, Either<A, B>>) => Separated<HKT<F, A>, HKT<F, B>>
 }
 
-/**
- * @see Compactable
- * @since 1.7.0
- */
-export interface CompactableA<F, A, RL, RR> {
-  readonly URI: F
-  readonly _A: A
-  readonly _RL: RL
-  readonly _RR: RR
-  readonly compact: (fa: HKT<F, Option<A>>) => HKT<F, A>
-  readonly separate: (fa: HKT<F, Either<RL, RR>>) => Separated<HKT<F, RL>, HKT<F, RR>>
-}
-
 /**
  * @see Compactable
  * @since 1.7.0
@@ -58,19 +47,6 @@ export interface Compactable1<F extends URIS> {
   readonly separate: <A, B>(fa: Type<F, Either<A, B>>) => Separated<Type<F, A>, Type<F, B>>
 }
 
-/**
- * @see Compactable
- * @since 1.7.0
- */
-export interface Compactable1A<F extends URIS, A, RL, RR> {
-  readonly URI: F
-  readonly _A: A
-  readonly _RL: RL
-  readonly _RR: RR
-  readonly compact: (fa: Type<F, Option<A>>) => Type<F, A>
-  readonly separate: (fa: Type<F, Either<RL, RR>>) => Separated<Type<F, RL>, Type<F, RR>>
-}
-
 /**
  * @see Compactable
  * @since 1.7.0
@@ -119,51 +95,105 @@ export interface Compactable3C<F extends URIS3, U, L> {
  * @function
  * @since 1.7.0
  */
-export function compactDefault<F extends URIS3, U, L>(
+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 compactDefault<F extends URIS3>(
+export function getCompactFromSeparate<F extends URIS3>(
   F: Functor3<F> & Pick<Compactable3<F>, 'separate'>
 ): Compactable3<F>['compact']
-export function compactDefault<F extends URIS2, L>(
+export function getCompactFromSeparate<F extends URIS2, L>(
   F: Functor2C<F, L> & Pick<Compactable2C<F, L>, 'separate'>
 ): Compactable2C<F, L>['compact']
-export function compactDefault<F extends URIS2>(
+export function getCompactFromSeparate<F extends URIS2>(
   F: Functor2<F> & Pick<Compactable2<F>, 'separate'>
 ): Compactable2<F>['compact']
-export function compactDefault<F extends URIS>(
+export function getCompactFromSeparate<F extends URIS>(
   F: Functor1<F> & Pick<Compactable1<F>, 'separate'>
 ): Compactable1<F>['compact']
-export function compactDefault<F>(F: Functor<F> & Pick<Compactable<F>, 'separate'>): Compactable<F>['compact']
-export function compactDefault<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']
+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 separateDefault<F extends URIS3, U, L>(
+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 separateDefault<F extends URIS3>(
+export function getSeparateFromCompact<F extends URIS3>(
   F: Functor3<F> & Pick<Compactable3<F>, 'compact'>
 ): Compactable3<F>['separate']
-export function separateDefault<F extends URIS2, L>(
+export function getSeparateFromCompact<F extends URIS2, L>(
   F: Functor2C<F, L> & Pick<Compactable2C<F, L>, 'compact'>
 ): Compactable2C<F, L>['separate']
-export function separateDefault<F extends URIS2>(
+export function getSeparateFromCompact<F extends URIS2>(
   F: Functor2<F> & Pick<Compactable2<F>, 'compact'>
 ): Compactable2<F>['separate']
-export function separateDefault<F extends URIS>(
+export function getSeparateFromCompact<F extends URIS>(
   F: Functor1<F> & Pick<Compactable1<F>, 'compact'>
 ): Compactable1<F>['separate']
-export function separateDefault<F>(F: Functor<F> & Pick<Compactable<F>, 'compact'>): Compactable<F>['separate']
-export function separateDefault<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']
+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/Filterable.ts b/src/Filterable.ts
new file mode 100644
index 00000000..0a63fc77
--- /dev/null
+++ b/src/Filterable.ts
@@ -0,0 +1,338 @@
+import { Functor, Functor1, Functor2, Functor2C, Functor3, Functor3C } from './Functor'
+import {
+  Compactable,
+  Compactable1,
+  Compactable2,
+  Compactable2C,
+  Compactable3,
+  Compactable3C,
+  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'
+
+/**
+ * @typeclass
+ * `Filterable` represents data structures which can be _partitioned_/_filtered_.
+ * - {@link partitionMap}
+ * - {@link partition}
+ * - {@link filterMap}
+ * - {@link filter}
+ *
+ * @see https://github.com/LiamGoodacre/purescript-filterable/blob/master/src/Data/Filterable.purs
+ * @since 1.7.0
+ */
+export interface Filterable<F> extends Functor<F>, Compactable<F> {
+  /**
+   * Partition a data structure based on an either predicate.
+   * @since 1.7.0
+   */
+  readonly partitionMap: <RL, RR, A>(fa: HKT<F, A>, f: (a: A) => Either<RL, RR>) => Separated<HKT<F, RL>, HKT<F, RR>>
+  /**
+   * Partition a data structure based on boolean predicate.
+   * @since 1.7.0
+   */
+  readonly partition: <A>(fa: HKT<F, A>, p: Predicate<A>) => Separated<HKT<F, A>, HKT<F, A>>
+  /**
+   * Map over a data structure and filter based on a maybe.
+   * @since 1.7.0
+   */
+  readonly filterMap: <A, B>(fa: HKT<F, A>, f: (a: A) => Option<B>) => HKT<F, B>
+  /**
+   * Filter a data structure based on a boolean.
+   * @since 1.7.0
+   */
+  readonly filter: <A>(fa: HKT<F, A>, p: Predicate<A>) => HKT<F, A>
+}
+
+/**
+ * @since 1.7.0
+ * @see Filterable
+ */
+export interface Filterable1<F extends URIS> extends Functor1<F>, Compactable1<F> {
+  readonly partitionMap: <RL, RR, A>(fa: Type<F, A>, f: (a: A) => Either<RL, RR>) => Separated<Type<F, RL>, Type<F, RR>>
+  readonly partition: <A>(fa: Type<F, A>, p: Predicate<A>) => Separated<Type<F, A>, Type<F, A>>
+  readonly filterMap: <A, B>(fa: Type<F, A>, f: (a: A) => Option<B>) => Type<F, B>
+  readonly filter: <A>(fa: Type<F, A>, p: Predicate<A>) => Type<F, A>
+}
+
+/**
+ * @since 1.7.0
+ * @see Filterable
+ */
+export interface Filterable2<F extends URIS2> extends Functor2<F>, Compactable2<F> {
+  readonly partitionMap: <RL, RR, L, A>(
+    fa: Type2<F, L, A>,
+    f: (a: A) => Either<RL, RR>
+  ) => Separated<Type2<F, L, RL>, Type2<F, L, RR>>
+  readonly partition: <L, A>(fa: Type2<F, L, A>, p: Predicate<A>) => Separated<Type2<F, L, A>, Type2<F, L, A>>
+  readonly filterMap: <L, A, B>(fa: Type2<F, L, A>, f: (a: A) => Option<B>) => Type2<F, L, B>
+  readonly filter: <L, A>(fa: Type2<F, L, A>, p: Predicate<A>) => Type2<F, L, A>
+}
+
+/**
+ * @since 1.7.0
+ * @see Filterable
+ */
+export interface Filterable2C<F extends URIS2, L> extends Functor2C<F, L>, Compactable2C<F, L> {
+  readonly partitionMap: <RL, RR, A>(
+    fa: Type2<F, L, A>,
+    f: (a: A) => Either<RL, RR>
+  ) => Separated<Type2<F, L, RL>, Type2<F, L, RR>>
+  readonly partition: <A>(fa: Type2<F, L, A>, p: Predicate<A>) => Separated<Type2<F, L, A>, Type2<F, L, A>>
+  readonly filterMap: <A, B>(fa: Type2<F, L, A>, f: (a: A) => Option<B>) => Type2<F, L, B>
+  readonly filter: <A>(fa: Type2<F, L, A>, p: Predicate<A>) => Type2<F, L, A>
+}
+
+/**
+ * @since 1.7.0
+ * @see Filterable
+ */
+export interface Filterable3<F extends URIS3> extends Functor3<F>, Compactable3<F> {
+  readonly partitionMap: <RL, RR, U, L, A>(
+    fa: Type3<F, U, L, A>,
+    f: (a: A) => Either<RL, RR>
+  ) => Separated<Type3<F, U, L, RL>, Type3<F, U, L, RR>>
+  readonly partition: <U, L, A>(
+    fa: Type3<F, U, L, A>,
+    p: Predicate<A>
+  ) => Separated<Type3<F, U, L, A>, Type3<F, U, L, A>>
+  readonly filterMap: <U, L, A, B>(fa: Type3<F, U, L, A>, f: (a: A) => Option<B>) => Type3<F, U, L, B>
+  readonly filter: <U, L, A>(fa: Type3<F, U, L, A>, p: Predicate<A>) => Type3<F, U, L, A>
+}
+
+/**
+ * @since 1.7.0
+ * @see Filterable
+ */
+export interface Filterable3C<F extends URIS3, U, L> extends Functor3C<F, U, L>, Compactable3C<F, U, L> {
+  readonly partitionMap: <RL, RR, A>(
+    fa: Type3<F, U, L, A>,
+    f: (a: A) => Either<RL, RR>
+  ) => Separated<Type3<F, U, L, RL>, Type3<F, U, L, RR>>
+  readonly partition: <A>(fa: Type3<F, U, L, A>, p: Predicate<A>) => Separated<Type3<F, U, L, A>, Type3<F, U, L, A>>
+  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/index.ts b/src/index.ts
index b93691e9..934fdfa0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -150,3 +150,5 @@ import * as writer from './Writer'
 export { writer }
 import * as compactable from './Compactable'
 export { compactable }
+import * as filterable from './Filterable'
+export { filterable }


Test patch:
diff --git a/test/Compactable.ts b/test/Compactable.ts
index 465bac5e..a9e32e82 100644
--- a/test/Compactable.ts
+++ b/test/Compactable.ts
@@ -1,51 +1,57 @@
 import * as assert from 'assert'
-import { Either, left, right } from '../src/Either'
-import { none, some, Option, option } from '../src/Option'
-import { compactDefault, Separated, separateDefault } from '../src/Compactable'
-import { identity } from '../src/function'
+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', () => {
-  const separate = <L, A>(fa: Option<Either<L, A>>): Separated<Option<L>, Option<A>> =>
-    fa.foldL(
-      () => ({
-        left: none,
-        right: none
-      }),
-      e =>
-        e.fold<Separated<Option<L>, Option<A>>>(
-          l => ({
-            left: some(l),
-            right: none
-          }),
-          r => ({
-            left: none,
-            right: some(r)
-          })
-        )
-    )
-
-  const compact = <A>(fa: Option<Option<A>>): Option<A> => fa.chain(identity)
-
-  it('compactDefault', () => {
-    const F = {
-      ...option,
+  it('getCompactFromSeparate', () => {
+    const { URI, map, separate } = array
+    const compactF = getCompactFromSeparate({
+      URI,
+      map,
       separate
-    }
-    const compactDefaultF = compactDefault(F)
-    assert.deepEqual(compactDefaultF(none), compact(none))
-    assert.deepEqual(compactDefaultF(some(none)), compact(some(none)))
-    assert.deepEqual(compactDefaultF(some(some(123))), compact(some(some(123))))
+    })
+    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('separateDefault', () => {
-    const F = {
-      ...option,
+  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
+    })
 
-    const separateDefaultF = separateDefault(F)
-    assert.deepEqual(separateDefaultF(none), separate(none))
-    assert.deepEqual(separateDefaultF(some(left('123'))), separate(some(left('123'))))
-    assert.deepEqual(separateDefaultF(some(right('123'))), separate(some(right('123'))))
+    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/Filterable.ts b/test/Filterable.ts
new file mode 100644
index 00000000..f38c5811
--- /dev/null
+++ b/test/Filterable.ts
@@ -0,0 +1,133 @@
+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 926c2bd2..3965fd64 100644
--- a/test/Option.ts
+++ b/test/Option.ts
@@ -25,6 +25,8 @@ import { setoidNumber } from '../src/Setoid'
 import { traverse } from '../src/Traversable'
 import { identity } from '../src/function'
 
+const p = (n: number): boolean => n > 2
+
 describe('Option', () => {
   it('fold', () => {
     const f = 'none'
@@ -180,7 +182,7 @@ describe('Option', () => {
   })
 
   it('fromPredicate', () => {
-    const f = fromPredicate((n: number) => n > 2)
+    const f = fromPredicate(p)
     assert.deepEqual(f(1), none)
     assert.deepEqual(f(3), some(3))
   })