Repository: gcanti/fp-ts
Pull request #480 (2018-06-13 05:46:20)

Internal: add Applicative2C overloadings to Traversable.traverse


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/HKT.md b/HKT.md
index d5c0c105..21f9485a 100644
--- a/HKT.md
+++ b/HKT.md
@@ -107,7 +107,7 @@ Adding an entry means to leverage the module augmentation feature
 
 declare module './HKT' {
   interface URI2HKT<A> {
-    Identity: Identity<A> // maps the key "Identity" to the type constructor `Identity`
+    Identity: Identity<A> // maps the key "Identity" to the type `Identity`
   }
 }
 ```
diff --git a/README.md b/README.md
index 42766414..c9b5c180 100644
--- a/README.md
+++ b/README.md
@@ -34,52 +34,52 @@ npm install --save fp-ts
 
 # TypeScript compatibility
 
-The stable version is tested against TypeScript 2.8.x, but should run with TypeScript 2.2.2+ too
+The stable version is tested against TypeScript 2.9.x, but should run with TypeScript 2.2.2+ too
 
 # Ecosystem
 
-* [io-ts](https://github.com/gcanti/io-ts) - TypeScript compatible runtime type system for IO validation
-* [monocle-ts](https://github.com/gcanti/monocle-ts) - Functional optics: a (partial) porting of scala monocle to
+- [io-ts](https://github.com/gcanti/io-ts) - TypeScript compatible runtime type system for IO validation
+- [monocle-ts](https://github.com/gcanti/monocle-ts) - Functional optics: a (partial) porting of scala monocle to
   TypeScript
-* [newtype-ts](https://github.com/gcanti/newtype-ts) - Implementation of newtypes in TypeScript
-* [logging-ts](https://github.com/gcanti/logging-ts) - Composable loggers for TypeScript
-* [fp-ts-routing](https://github.com/gcanti/fp-ts-routing) - A type-safe bidirectional routing library for TypeScript
-* [parser-ts](https://github.com/gcanti/parser-ts) - String parser combinators for TypeScript
-* [remote-data-ts](https://github.com/devex-web-frontend/remote-data-ts) - RemoteData type (check [this article](https://medium.com/@gcanti/slaying-a-ui-antipattern-with-flow-5eed0cfb627b))
+- [newtype-ts](https://github.com/gcanti/newtype-ts) - Implementation of newtypes in TypeScript
+- [logging-ts](https://github.com/gcanti/logging-ts) - Composable loggers for TypeScript
+- [fp-ts-routing](https://github.com/gcanti/fp-ts-routing) - A type-safe bidirectional routing library for TypeScript
+- [parser-ts](https://github.com/gcanti/parser-ts) - String parser combinators for TypeScript
+- [remote-data-ts](https://github.com/devex-web-frontend/remote-data-ts) - RemoteData type (check [this article](https://medium.com/@gcanti/slaying-a-ui-antipattern-with-flow-5eed0cfb627b))
 
 ## Bindings
 
-* [fp-ts-rxjs](https://github.com/gcanti/fp-ts-rxjs) - fp-ts bindings for RxJS
-* [fp-ts-fluture](https://github.com/gcanti/fp-ts-fluture) - fp-ts bindings for Fluture
+- [fp-ts-rxjs](https://github.com/gcanti/fp-ts-rxjs) - fp-ts bindings for RxJS
+- [fp-ts-fluture](https://github.com/gcanti/fp-ts-fluture) - fp-ts bindings for Fluture
 
 # Documentation
 
-* [API Reference](docs/index.md)
+- [API Reference](docs/index.md)
 
 ## Internals
 
-* [How `HKT`, `URI2HKT`, `URIS` and `Type` work](HKT.md)
+- [How `HKT`, `URI2HKT`, `URIS` and `Type` work](HKT.md)
 
 ## Examples
 
-* [Free monad](examples/Free.ts)
-* [MTL-style](examples/mtl.ts)
-* OptionT monad transformer
-  * [ArrayOption](examples/ArrayOption.ts)
-  * [TaskOption](examples/TaskOption.ts)
-* EitherT monad transformer
-  * [EitherOption](examples/EitherOption.ts)
-* StateT monad transformer
-  * [StateIO](examples/StateIO.ts)
-  * [StateTaskEither](examples/StateTaskEither.ts)
-* ReaderT monad transformer
-  * [ReaderIO](examples/ReaderIO.ts)
-* Applicative composition
-  * [TaskValidation](examples/TaskValidation.ts)
-* [Type safe finite state machines with `IxIO`](examples/ixIO.ts)
-* [Moore machines](examples/Moore.ts)
-* [Debugging with `Trace`](examples/debugging-with-Trace.ts)
-* [fantas-eel-and-specification](fantas-eel-and-specification) - Code for the
+- [Free monad](examples/Free.ts)
+- [MTL-style](examples/mtl.ts)
+- OptionT monad transformer
+  - [ArrayOption](examples/ArrayOption.ts)
+  - [TaskOption](examples/TaskOption.ts)
+- EitherT monad transformer
+  - [EitherOption](examples/EitherOption.ts)
+- StateT monad transformer
+  - [StateIO](examples/StateIO.ts)
+  - [StateTaskEither](examples/StateTaskEither.ts)
+- ReaderT monad transformer
+  - [ReaderIO](examples/ReaderIO.ts)
+- Applicative composition
+  - [TaskValidation](examples/TaskValidation.ts)
+- [Type safe finite state machines with `IxIO`](examples/ixIO.ts)
+- [Moore machines](examples/Moore.ts)
+- [Debugging with `Trace`](examples/debugging-with-Trace.ts)
+- [fantas-eel-and-specification](fantas-eel-and-specification) - Code for the
   [Fantas, Eel, and Specification](http://www.tomharding.me/2017/03/03/fantas-eel-and-specification/) blog series on
   functional programming by Tom Harding
 
diff --git a/docs-generator/code2comment.html b/docs-generator/code2comment.html
index 64271dd4..a9b217f1 100644
--- a/docs-generator/code2comment.html
+++ b/docs-generator/code2comment.html
@@ -21,7 +21,7 @@
     }
     function t(src) {
       const lines = src.split('\n').map(line => '* ' + line.replace('../src/', 'fp-ts/lib/'))
-      return '*\n* Example\n*\n* ```ts\n' + lines.join('\n') + '\n* ```\n*'
+      return '*\n* @example\n*\n* ' + lines.join('\n') + '\n*'
     }
   </script>
 </body>
diff --git a/docs-generator/domain.ts b/docs-generator/domain.ts
index e164fa52..c2acd7f0 100644
--- a/docs-generator/domain.ts
+++ b/docs-generator/domain.ts
@@ -7,7 +7,8 @@ export class Method {
     readonly signature: string,
     readonly description: Option<string>,
     readonly since: string,
-    readonly example: Option<string>
+    readonly example: Option<string>,
+    readonly deprecated: boolean
   ) {}
 }
 
@@ -25,7 +26,8 @@ export class Data {
     readonly signature: string,
     readonly description: Option<string>,
     readonly constructors: Array<Constructor>,
-    readonly since: string
+    readonly since: string,
+    readonly example: Option<string>
   ) {}
 }
 
@@ -39,7 +41,8 @@ export class Func {
     readonly description: Option<string>,
     readonly isAlias: boolean,
     readonly since: string,
-    readonly example: Option<string>
+    readonly example: Option<string>,
+    readonly deprecated: boolean
   ) {}
 }
 
diff --git a/docs-generator/main.ts b/docs-generator/main.ts
index 1085b372..4ad76dc0 100644
--- a/docs-generator/main.ts
+++ b/docs-generator/main.ts
@@ -7,6 +7,7 @@ import { Env, parseModule, ParseError } from './parser'
 import { write, indexOutputPath, readModule, writeModule } from './fs'
 import chalk from 'chalk'
 import Ast from 'ts-simple-ast'
+// tslint:disable-next-line:no-duplicate-imports
 import { SourceFile } from 'ts-simple-ast'
 
 const printError = (error: ParseError): string => {
diff --git a/docs-generator/markdown.ts b/docs-generator/markdown.ts
index 50734973..ace1da47 100644
--- a/docs-generator/markdown.ts
+++ b/docs-generator/markdown.ts
@@ -29,6 +29,7 @@ export const code = (code: string) => '`' + code + '`'
 export const link = (text: string, href: string) => `[${text}](${href})`
 export const ts = fence('ts')
 export const italic = (code: string) => '*' + code + '*'
+export const strike = (text: string) => '~~' + text + '~~'
 
 export const header = (id: string, title: string): string => {
   return `---
@@ -98,8 +99,10 @@ const printExample = (example: Option<string>): string =>
 
 const printSignature = (signature: string): string => CRLF + CRLF + italic('Signature') + CRLF + ts(signature)
 
+const handleDeprecated = (s: string, deprecated: boolean): string => deprecated ? strike(s) + ' (deprecated)' : s
+
 const printMethod = (m: Method): string => {
-  let s = CRLF + h3(m.name)
+  let s = CRLF + h3(handleDeprecated(m.name, m.deprecated))
   s += CRLF + italic('method')
   s += CRLF + CRLF + italic(`since ${m.since}`)
   s += CRLF + printSignature(m.signature)
@@ -109,7 +112,7 @@ const printMethod = (m: Method): string => {
 }
 
 const printData = (d: Data): string => {
-  let s = `\n${h3(d.name)}`
+  let s = CRLF + h3(d.name)
   s += CRLF + italic('data')
   s += CRLF + CRLF + italic(`since ${d.since}`)
   s += CRLF + printSignature(d.signature)
@@ -122,11 +125,12 @@ const printData = (d: Data): string => {
         .map(m => printMethod(m))
         .join('')
   }
+  s += printExample(d.example)
   return s
 }
 
 const printInstance = (i: Instance): string => {
-  let s = `\n${h3(i.name)}`
+  let s = CRLF + h3(i.name)
   s += CRLF + italic('instance')
   s += CRLF + CRLF + italic(`since ${i.since}`)
   s += CRLF + printSignature(i.signature)
@@ -135,7 +139,7 @@ const printInstance = (i: Instance): string => {
 }
 
 const printConstant = (c: Constant): string => {
-  let s = `\n${h3(c.name)}`
+  let s = CRLF + h3(c.name)
   s += CRLF + italic('constant')
   s += CRLF + CRLF + italic(`since ${c.since}`)
   s += CRLF + printSignature(c.signature)
@@ -144,7 +148,7 @@ const printConstant = (c: Constant): string => {
 }
 
 const printFunc = (f: Func): string => {
-  let s = `\n${h3(f.name)}`
+  let s = CRLF + h3(handleDeprecated(f.name, f.deprecated))
   s += CRLF + italic('function')
   s += CRLF + CRLF + italic(`since ${f.since}`)
   if (f.isAlias) {
@@ -157,7 +161,7 @@ const printFunc = (f: Func): string => {
 }
 
 const printTypeclass = (tc: Typeclass): string => {
-  let s = `\n${h3(tc.name)}`
+  let s = CRLF + h3(tc.name)
   s += CRLF + italic('type class')
   s += CRLF + printSignature(tc.signature)
   s += printDescription(tc.description)
diff --git a/docs-generator/parser.ts b/docs-generator/parser.ts
index 8646db94..b3bd10bb 100644
--- a/docs-generator/parser.ts
+++ b/docs-generator/parser.ts
@@ -88,13 +88,15 @@ const getMethod = (md: MethodDeclaration): Method => {
   const annotation = getAnnotation(md.getDocumentationCommentNodes())
   const since = getSince(annotation)
   const example = getExample(annotation)
+  const deprecated = getDeprecated(annotation)
   return {
     type: 'method',
     name,
     signature,
     description,
     since: since.getOrElse('1.0.0'),
-    example
+    example,
+    deprecated
   }
 }
 
@@ -142,6 +144,14 @@ const getExample = (annotation: Annotation): Option<string> => {
   return fromNullable(annotation.tags.filter(isExampleTag)[0]).mapNullable(tag => tag.description)
 }
 
+const isDeprecatedTag = (tag: Tag): boolean => {
+  return tag.title === 'deprecated'
+}
+
+const getDeprecated = (annotation: Annotation): boolean => {
+  return fromNullable(annotation.tags.filter(isDeprecatedTag)[0]).isSome()
+}
+
 /** parses data types which are unions */
 const parseTypeAliasDeclarationData = (tad: TypeAliasDeclaration): ParseResult<Export> => {
   return new ReaderEither(e => {
@@ -168,10 +178,11 @@ const parseTypeAliasDeclarationData = (tad: TypeAliasDeclaration): ParseResult<E
       }
       const constructors = rights(eitherConstructors)
       const since = getSince(annotation)
+      const example = getExample(annotation)
       if (since.isNone()) {
         return ko(new SinceMissing(e.currentModuleName, dataName))
       } else {
-        return ok(new Data(dataName, signature, description, constructors, since.value))
+        return ok(new Data(dataName, signature, description, constructors, since.value, example))
       }
     }
     return notFound
@@ -189,10 +200,11 @@ const parseClassDeclarationData = (c: ClassDeclaration): ParseResult<Export> =>
       const methods = getClassMethods(c)
       const constructors = [new Constructor(dataName, methods)]
       const since = getSince(annotation)
+      const example = getExample(annotation)
       if (since.isNone()) {
         return ko(new SinceMissing(e.currentModuleName, dataName))
       } else {
-        return ok(new Data(dataName, signature, description, constructors, since.value))
+        return ok(new Data(dataName, signature, description, constructors, since.value, example))
       }
     }
     return notFound
@@ -276,10 +288,11 @@ const parseFunctionVariableDeclaration = (vd: VariableDeclaration): ParseResult<
           const signature = text.substring(start, end.getOrElse(text.length))
           const since = getSince(annotation)
           const example = getExample(annotation)
+          const deprecated = getDeprecated(annotation)
           if (since.isNone()) {
             return ko(new SinceMissing(e.currentModuleName, name))
           } else {
-            return ok(new Func(name, signature, description, isAlias(annotation), since.value, example))
+            return ok(new Func(name, signature, description, isAlias(annotation), since.value, example,deprecated))
           }
         }
       }
@@ -313,10 +326,11 @@ const parseFunctionDeclaration = (f: FunctionDeclaration): ParseResult<Export> =
       const signature = text.substring(start, end === -1 ? text.length : end)
       const since = getSince(annotation)
       const example = getExample(annotation)
+      const deprecated = getDeprecated(annotation)
       if (since.isNone()) {
         return ko(new SinceMissing(e.currentModuleName, name))
       } else {
-        return ok(new Func(name, signature, description, false, since.value, example))
+        return ok(new Func(name, signature, description, false, since.value, example, deprecated))
       }
     }
     return notFound
diff --git a/docs/Validation.md b/docs/Validation.md
index 717cfba6..36564952 100644
--- a/docs/Validation.md
+++ b/docs/Validation.md
@@ -187,6 +187,46 @@ _Signature_
 (): string
 ```
 
+_Example_
+
+```ts
+import { Validation, getApplicative, success, failure } from 'fp-ts/lib/Validation'
+import { NonEmptyArray, getSemigroup } from 'fp-ts/lib/NonEmptyArray'
+
+interface Person {
+  readonly name: string
+  readonly age: number
+}
+
+// curried constructor
+const person = (name: string) => (age: number): Person => ({ name, age })
+
+// validators
+function validateName(input: string): Validation<NonEmptyArray<string>, string> {
+  return input.length === 0 ? failure(new NonEmptyArray('Invalid name: empty string', [])) : success(input)
+}
+function validateAge(input: string): Validation<NonEmptyArray<string>, number> {
+  const n = parseFloat(input)
+  if (isNaN(n)) {
+    return failure(new NonEmptyArray(`Invalid age: not a number ${input}`, []))
+  }
+  return n % 1 !== 0 ? failure(new NonEmptyArray(`Invalid age: not an integer ${n}`, [])) : success(n)
+}
+
+// get an `Applicative` instance for Validation<NonEmptyArray<string>, ?>
+const A = getApplicative(getSemigroup<string>())
+
+function validatePerson(input: Record<string, string>): Validation<NonEmptyArray<string>, Person> {
+  return A.ap(validateName(input['name']).map(person), validateAge(input['age']))
+}
+
+console.log(validatePerson({ name: '', age: '1.2' }))
+// failure(new NonEmptyArray("Invalid name: empty string", ["Invalid age: not an integer 1.2"]))
+
+console.log(validatePerson({ name: 'Giulio', age: '44' }))
+// success({ "name": "Giulio", "age": 44 })
+```
+
 ## Instances
 
 ### validation
diff --git a/examples/ixIO.ts b/examples/ixIO.ts
index a30ec96c..f1206f65 100644
--- a/examples/ixIO.ts
+++ b/examples/ixIO.ts
@@ -1,4 +1,4 @@
-import { IO } from '../src/IO'
+import { log } from '../src/Console'
 import { IxIO } from '../src/IxIO'
 
 /*
@@ -32,29 +32,17 @@ export class Operation<I extends DoorState, O extends DoorState, A> extends IxIO
 
 class Open extends Operation<'Closed', 'Open', void> {
   constructor() {
-    super(
-      new IO(() => {
-        console.log(`Opening the door`)
-      })
-    )
+    super(log('Opening the door'))
   }
 }
 class Close extends Operation<'Open', 'Closed', void> {
   constructor() {
-    super(
-      new IO(() => {
-        console.log(`Closing the door`)
-      })
-    )
+    super(log('Closing the door'))
   }
 }
 class RingBell extends Operation<'Closed', 'Closed', void> {
   constructor() {
-    super(
-      new IO(() => {
-        console.log(`Ringing the bell`)
-      })
-    )
+    super(log('Ringing the bell'))
   }
 }
 
diff --git a/examples/tsconfig.json b/examples/tsconfig.json
deleted file mode 100644
index 65fc306e..00000000
--- a/examples/tsconfig.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "compilerOptions": {
-    "noEmit": true,
-    "declaration": true,
-    "module": "commonjs",
-    "strict": true,
-    "noImplicitReturns": false,
-    "noUnusedLocals": true,
-    "noUnusedParameters": false,
-    "noFallthroughCasesInSwitch": true,
-    "noEmitOnError": false,
-    "allowJs": false,
-    "target": "es5",
-    "moduleResolution": "node",
-    "forceConsistentCasingInFileNames": true,
-    "lib": ["es6", "dom"]
-  },
-  "include": ["../src/**/*", "./**/*"]
-}
diff --git a/exercises/answer1.ts b/exercises/answer1.ts
index c6174fe7..894b74c9 100644
--- a/exercises/answer1.ts
+++ b/exercises/answer1.ts
@@ -1,5 +1,4 @@
-import { Ord } from 'fp-ts/lib/Ord'
-import { ordNumber, ordString } from 'fp-ts/lib/Ord'
+import { Ord, ordNumber, ordString } from '../src/Ord'
 
 export function binarySearch<A>(xs: Array<A>, x: A, ord: Ord<A>): number {
   function go(low: number, mid: number, high: number): number {
diff --git a/exercises/answer2.ts b/exercises/answer2.ts
index 0fe6dc72..aa4fc5d6 100644
--- a/exercises/answer2.ts
+++ b/exercises/answer2.ts
@@ -1,5 +1,4 @@
-import { Ord } from 'fp-ts/lib/Ord'
-import { greaterThan, ordNumber } from 'fp-ts/lib/Ord'
+import { greaterThan, Ord, ordNumber } from '../src/Ord'
 
 export function isSorted<A>(xs: Array<A>, ord: Ord<A>): boolean {
   const len = xs.length
diff --git a/exercises/answer3.ts b/exercises/answer3.ts
index fb45f76c..e48fca3d 100644
--- a/exercises/answer3.ts
+++ b/exercises/answer3.ts
@@ -1,6 +1,6 @@
-import { Functor1 } from 'fp-ts/lib/Functor'
+import { Functor1 } from '../src/Functor'
 
-declare module 'fp-ts/lib/HKT' {
+declare module '../src/HKT' {
   interface URI2HKT<A> {
     List: List<A>
   }
diff --git a/exercises/answer4.ts b/exercises/answer4.ts
index 47829ef6..0394129c 100644
--- a/exercises/answer4.ts
+++ b/exercises/answer4.ts
@@ -1,4 +1,5 @@
-import { Option, none, option } from 'fp-ts/lib/Option'
+import { Either, left, right } from '../src/Either'
+import { none, Option, option } from '../src/Option'
 
 export function head<A>(xs: Array<A>): Option<A> {
   if (xs.length) {
@@ -10,17 +11,14 @@ export function head<A>(xs: Array<A>): Option<A> {
 console.log(head([1, 2, 3])) // => some(1)
 console.log(head([])) // => none
 
-import { Either } from 'fp-ts/lib/Either'
-import * as either from 'fp-ts/lib/Either'
-
 export function elementAt<A>(xs: Array<A>, i: number): Either<string, A> {
   if (i < 0) {
-    return either.left<string, A>('out of lower bound')
+    return left<string, A>('out of lower bound')
   }
   if (i >= xs.length) {
-    return either.left<string, A>('out of upper bound')
+    return left<string, A>('out of upper bound')
   }
-  return either.right<string, A>(xs[i])
+  return right<string, A>(xs[i])
 }
 
 console.log(elementAt([1, 2, 3], -1)) // => left('out of lower bound')
diff --git a/exercises/answer5.ts b/exercises/answer5.ts
index 35d148ed..61b41d7a 100644
--- a/exercises/answer5.ts
+++ b/exercises/answer5.ts
@@ -1,6 +1,6 @@
-import { Option, some, none, option } from 'fp-ts/lib/Option'
-import { array } from 'fp-ts/lib/Array'
-import { sequence } from 'fp-ts/lib/Traversable'
+import { Option, some, none, option } from '../src/Option'
+import { array } from '../src/Array'
+import { sequence } from '../src/Traversable'
 
 export function getAllSomesOrNone<A>(xs: Array<Option<A>>): Option<Array<A>> {
   return sequence(option, array)(xs)
diff --git a/exercises/answer6.ts b/exercises/answer6.ts
index 6461712e..cdb6d797 100644
--- a/exercises/answer6.ts
+++ b/exercises/answer6.ts
@@ -1,6 +1,6 @@
-import { array } from 'fp-ts/lib/Array'
-import { some, none } from 'fp-ts/lib/Option'
-import { tuple } from 'fp-ts/lib/function'
+import { array } from '../src/Array'
+import { some, none } from '../src/Option'
+import { tuple } from '../src/function'
 
 export function evens(count: number): Array<number> {
   let i = count
diff --git a/exercises/exercise2.ts b/exercises/exercise2.ts
index 0b020fa1..dcb5df74 100644
--- a/exercises/exercise2.ts
+++ b/exercises/exercise2.ts
@@ -6,6 +6,6 @@
 
 */
 
-import { Ord } from 'fp-ts/lib/Ord'
+import { Ord } from '../src/Ord'
 
 declare function isSorted<A>(xs: Array<A>, ord: Ord<A>): boolean
diff --git a/exercises/exercise4.ts b/exercises/exercise4.ts
index 0b8e7a8d..5ee68173 100644
--- a/exercises/exercise4.ts
+++ b/exercises/exercise4.ts
@@ -5,7 +5,7 @@
   Write a type safe head function.
 
 */
-import { Option } from 'fp-ts/lib/Option'
+import { Option } from '../src/Option'
 
 declare function head<A>(xs: Array<A>): Option<A>
 
@@ -14,6 +14,6 @@ declare function head<A>(xs: Array<A>): Option<A>
   Write a type safe elementAt function
 
 */
-import { Either } from 'fp-ts/lib/Either'
+import { Either } from '../src/Either'
 
 declare function elementAt<A>(xs: Array<A>, i: number): Either<string, A>
diff --git a/exercises/exercise5.ts b/exercises/exercise5.ts
index c8b0f01c..bd5de46b 100644
--- a/exercises/exercise5.ts
+++ b/exercises/exercise5.ts
@@ -8,6 +8,6 @@
   Some with a list of all the values
 
 */
-import { Option } from 'fp-ts/lib/Option'
+import { Option } from '../src/Option'
 
 declare function getAllSomesOrNone<A>(xs: Array<Option<A>>): Option<Array<A>>
diff --git a/exercises/tsconfig.json b/exercises/tsconfig.json
deleted file mode 100644
index ab297f58..00000000
--- a/exercises/tsconfig.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-  "compilerOptions": {
-    "noStrictGenericChecks": true,
-    "declaration": true,
-    "module": "commonjs",
-    "noImplicitAny": true,
-    "noImplicitReturns": false,
-    "noImplicitThis": false,
-    "noUnusedLocals": false,
-    "noUnusedParameters": false,
-    "noFallthroughCasesInSwitch": true,
-    "noEmitOnError": false,
-    "allowJs": false,
-    "strictNullChecks": true,
-    "target": "es5",
-    "moduleResolution": "node",
-    "forceConsistentCasingInFileNames": true,
-    "lib": ["es6", "dom"],
-    "baseUrl": ".",
-    "paths": {
-      "fp-ts/lib/*": ["../src/*"],
-      "fp-ts": ["../src/index"]
-    }
-  }
-}
diff --git a/fantas-eel-and-specification/1.ts b/fantas-eel-and-specification/1.ts
index a504b127..11755dab 100644
--- a/fantas-eel-and-specification/1.ts
+++ b/fantas-eel-and-specification/1.ts
@@ -23,16 +23,16 @@ export type Line = {
 
 // however in order to add methods to Coord we have to change the enconding using `class`
 
+/** A coordinate in 3D space */
 export class Coord {
-  /** A coordinate in 3D space */
   constructor(readonly x: number, readonly y: number, readonly z: number) {}
   translate(deltaX: number, deltaY: number, deltaZ: number) {
     return new Coord(this.x + deltaX, this.y + deltaY, this.x + deltaZ)
   }
 }
 
+/** A line between two coordinates */
 export class Line {
-  /** A line between two coordinates */
   constructor(readonly from: Coord, readonly to: Coord) {}
 }
 
diff --git a/fantas-eel-and-specification/11.ts b/fantas-eel-and-specification/11.ts
index 1e39d664..349d80cd 100644
--- a/fantas-eel-and-specification/11.ts
+++ b/fantas-eel-and-specification/11.ts
@@ -17,8 +17,8 @@ export type URI = typeof URI
 export class Leaf<A> {
   static value = new Leaf<never>()
   readonly _tag: 'Leaf' = 'Leaf'
-  readonly '-A': A
-  readonly '-URI' = URI
+  readonly _A!: A
+  readonly _URI!: URI
   private constructor() {}
   inspect(): string {
     return this.toString()
@@ -30,8 +30,8 @@ export class Leaf<A> {
 
 export class Node<A> {
   readonly _tag: 'Node' = 'Node'
-  readonly '-A': A
-  readonly '-URI' = URI
+  readonly _A!: A
+  readonly _URI!: URI
   constructor(readonly left: BTree<A>, readonly value: A, readonly right: BTree<A>) {}
   inspect(): string {
     return this.toString()
diff --git a/fantas-eel-and-specification/16.ts b/fantas-eel-and-specification/16.ts
index e961f7bc..3a443126 100644
--- a/fantas-eel-and-specification/16.ts
+++ b/fantas-eel-and-specification/16.ts
@@ -7,8 +7,8 @@ export const URI = 'RoseTree'
 export type URI = typeof URI
 
 export class RoseTree<A> {
-  readonly '-A': A
-  readonly '-URI': URI
+  readonly _A!: A
+  readonly _URI!: URI
   constructor(readonly root: A, readonly forest: Array<RoseTree<A>>) {}
   map<B>(f: (a: A) => B): RoseTree<B> {
     return new RoseTree(f(this.root), this.forest.map(rt => rt.map(f)))
@@ -25,7 +25,9 @@ console.log(
     myTree.extend(rt => {
       return rt.forest.length < 1
         ? { value: rt.root, color: 'RED' }
-        : rt.forest.length < 5 ? { value: rt.root, color: 'ORANGE' } : { value: rt.root, color: 'GREEN' }
+        : rt.forest.length < 5
+          ? { value: rt.root, color: 'ORANGE' }
+          : { value: rt.root, color: 'GREEN' }
     }),
     null,
     2
diff --git a/fantas-eel-and-specification/2.ts b/fantas-eel-and-specification/2.ts
index 59dc2b75..bc691b5f 100644
--- a/fantas-eel-and-specification/2.ts
+++ b/fantas-eel-and-specification/2.ts
@@ -2,14 +2,13 @@
 // Code for http://www.tomharding.me/2017/03/08/fantas-eel-and-specification-2/
 //
 
-import { zipWith } from '../src/Array'
+import { filter, zipWith } from '../src/Array'
+import { Setoid } from '../src/Setoid'
 
 const sum = (x: number, y: number) => x + y
 console.log(zipWith([1, 2], [4, 5, 6], sum))
 // => [5, 7]
 
-import { filter } from '../src/Array'
-
 const gt2 = (n: number) => n > 2
 console.log(filter([1, 2, 3], gt2))
 // => [3]
@@ -24,8 +23,6 @@ export function naiveEquals<A>(xs: Array<A>, ys: Array<A>): boolean {
 
 // let's write a better equals...
 
-import { Setoid } from '../src/Setoid'
-
 export const equals = <A>(S: Setoid<A>) => (xs: Array<A>) => (ys: Array<A>): boolean =>
   xs.length === ys.length && xs.every((x, i) => S.equals(x, ys[i])) // <- this too restrictive
 
diff --git a/fantas-eel-and-specification/3.5.ts b/fantas-eel-and-specification/3.5.ts
index e4b09251..60ac1909 100644
--- a/fantas-eel-and-specification/3.5.ts
+++ b/fantas-eel-and-specification/3.5.ts
@@ -2,15 +2,13 @@
 // Code for http://www.tomharding.me/2017/04/09/fantas-eel-and-specification-3.5/
 //
 
-import { Ord, lessThanOrEq } from '../src/Ord'
 import { fold } from '../src/Array'
+import { lessThanOrEq, Ord, ordNumber } from '../src/Ord'
 
 export const lte = <A>(ord: Ord<A>) => (xs: Array<A>, ys: Array<A>): boolean =>
   fold(xs, true, (head, tail) =>
     fold(ys, false, (head_, tail_) => lessThanOrEq(ord)(head, head_) && lte(ord)(tail, tail_))
   )
 
-import { ordNumber } from '../src/Ord'
-
 console.log(lte(ordNumber)([1, 2], [1])) // => false
 console.log(lte(ordNumber)([1], [1, 2])) // => true
diff --git a/fantas-eel-and-specification/3.ts b/fantas-eel-and-specification/3.ts
index 97b10816..31bd6fce 100644
--- a/fantas-eel-and-specification/3.ts
+++ b/fantas-eel-and-specification/3.ts
@@ -2,7 +2,8 @@
 // Code for http://www.tomharding.me/2017/03/09/fantas-eel-and-specification-3/
 //
 
-import { Setoid } from '../src/Setoid'
+import { findIndex } from '../src/Array'
+import { Setoid, setoidNumber } from '../src/Setoid'
 
 export class Coord {
   /** A coordinate in 3D space */
@@ -72,9 +73,6 @@ export const notEquals = <A>(S: Setoid<A>) => (x: A) => (y: A): boolean => !S.eq
 // nub implementation
 //
 
-import { findIndex } from '../src/Array'
-import { setoidNumber } from '../src/Setoid'
-
 const nub = <A>(S: Setoid<A>) => (xs: Array<A>): Array<A> =>
   xs.filter((x, i) => findIndex(xs, (a: A) => S.equals(x, a)).exists(j => i === j))
 
diff --git a/fantas-eel-and-specification/4.ts b/fantas-eel-and-specification/4.ts
index 86062cc0..99b5e7c4 100644
--- a/fantas-eel-and-specification/4.ts
+++ b/fantas-eel-and-specification/4.ts
@@ -2,7 +2,7 @@
 // Code for http://www.tomharding.me/2017/03/13/fantas-eel-and-specification-4/
 //
 
-import { Semigroup } from '../src/Semigroup'
+import { fold, Semigroup } from '../src/Semigroup'
 
 export const semigroupString: Semigroup<string> = {
   concat: (x, y) => x + y // string concatenation
@@ -140,11 +140,9 @@ console.log(mySemigroup.concat(me, oldMe))
   name: 'Giulio',
   favouriteThings: [ 'climbing', 'math' ],
   registrationDate: 10,
-  hasMadePurchase: true }
+  hasMadePurchase: true
+}
 */
 
 // What if we want to merge more than two customers?
-
-import { fold } from '../src/Semigroup'
-
 console.log(fold(merge(strategy, iso))(me)([oldMe /* ... */]))
diff --git a/fantas-eel-and-specification/5.ts b/fantas-eel-and-specification/5.ts
index cb52f940..1820436b 100644
--- a/fantas-eel-and-specification/5.ts
+++ b/fantas-eel-and-specification/5.ts
@@ -2,8 +2,9 @@
 // Code for http://www.tomharding.me/2017/03/21/fantas-eel-and-specification-5/
 //
 
+import { fold, getFunctionMonoid, monoidAll, monoidString, monoidSum } from '../src/Monoid'
 import { getFirstMonoid, none, some } from '../src/Option'
-import { monoidSum } from '../src/Monoid'
+import { getMonoid } from '../src/Tuple'
 
 const firstMonoid = getFirstMonoid<number>()
 
@@ -13,8 +14,6 @@ console.log(firstMonoid.concat(none, some(1)))
 console.log(firstMonoid.concat(some(1), some(2)))
 // => some(1)
 
-import { fold } from '../src/Monoid'
-
 const sumAll = fold(monoidSum)
 
 console.log(sumAll([1, 2, 3, 4, 5]))
@@ -23,16 +22,11 @@ console.log(sumAll([1, 2, 3, 4, 5]))
 console.log(sumAll([]))
 // => 0
 
-import { getMonoid } from '../src/Tuple'
-import { monoidString } from '../src/Monoid'
-
 const monoidTuple = getMonoid(monoidSum, monoidString)
 
 console.log(monoidTuple.empty)
 // => new Tuple([0, ""])
 
-import { getFunctionMonoid, monoidAll } from '../src/Monoid'
-
 export const monoidFunction = getFunctionMonoid(monoidAll)<string>()
 
 export const gt2 = (s: string) => s.length > 2
diff --git a/fantas-eel-and-specification/6.ts b/fantas-eel-and-specification/6.ts
index 699a4540..92bd1a84 100644
--- a/fantas-eel-and-specification/6.ts
+++ b/fantas-eel-and-specification/6.ts
@@ -17,8 +17,8 @@ export const URI = 'MyType'
 export type URI = typeof URI
 
 export class MyType<A> {
-  readonly '-A': A
-  readonly '-URI': URI
+  readonly _A!: A
+  readonly _URI!: URI
   constructor(readonly value: A) {}
 }
 
diff --git a/fantas-eel-and-specification/9.ts b/fantas-eel-and-specification/9.ts
index 7b227199..dc6b68bf 100644
--- a/fantas-eel-and-specification/9.ts
+++ b/fantas-eel-and-specification/9.ts
@@ -2,9 +2,12 @@
 // Code for http://www.tomharding.me/2017/04/17/fantas-eel-and-specification-9/
 //
 
-import { liftA2 } from '../src/Apply'
 import { Applicative, Applicative1 } from '../src/Applicative'
-import { HKT, URIS, Type } from '../src/HKT'
+import { liftA2 } from '../src/Apply'
+import { HKT, Type, URIS } from '../src/HKT'
+import { getArrayMonoid, Monoid } from '../src/Monoid'
+import { getMonoid as getOptionMonoid, none, option, some } from '../src/Option'
+import * as tuple from '../src/Tuple'
 
 export const append = <A>(y: A) => (xs: Array<A>) => xs.concat([y])
 
@@ -12,12 +15,9 @@ export const append = <A>(y: A) => (xs: Array<A>) => xs.concat([y])
 export function insideOut<F extends URIS>(F: Applicative1<F>): <A>(xs: Array<Type<F, A>>) => Type<F, Array<A>>
 export function insideOut<F>(F: Applicative<F>): <A>(xs: Array<HKT<F, A>>) => HKT<F, Array<A>>
 export function insideOut<F>(F: Applicative<F>): <A>(xs: Array<HKT<F, A>>) => HKT<F, Array<A>> {
-  return <A>(xs: Array<HKT<F, A>>) =>
-    xs.reduce((acc, x) => liftA2(F)<A, Array<A>, Array<A>>(append)(x)(acc), F.of<A[]>([]))
+  return xs => xs.reduce((acc, x) => liftA2(F)(append)(x)(acc), F.of([]))
 }
 
-import { option, some, none, getMonoid as getOptionMonoid } from '../src/Option'
-
 console.log(insideOut(option)([some(2), some(10), some(3)])) // => some([2, 10, 3])
 
 console.log(insideOut(option)([some(2), none, some(3)])) // => none
@@ -26,15 +26,11 @@ console.log(insideOut(option)([some(2), none, some(3)])) // => none
 // Monoids from Applicatives
 //
 
-import { Monoid } from '../src/Monoid'
-
 export const getMonoid = <F extends URIS, A>(F: Applicative1<F>, M: Monoid<A>): Monoid<Type<F, A>> => ({
   concat: (x, y) => liftA2(F)((x: A) => (y: A) => M.concat(x, y))(x)(y),
   empty: F.of(M.empty)
 })
 
-import { getArrayMonoid } from '../src/Monoid'
-
 const monoidArrayNumber = getArrayMonoid<number>()
 
 // Usual implementation:
@@ -59,8 +55,6 @@ console.log(monoid.concat(none, none)) // => none
 // Tuple
 //
 
-import * as tuple from '../src/Tuple'
-
 const monoidTuple = tuple.getApplicative(monoidArrayNumber)
 
 console.log(monoidTuple.of(2)) // => new Tuple([[], 2])
diff --git a/fantas-eel-and-specification/tsconfig.json b/fantas-eel-and-specification/tsconfig.json
deleted file mode 100644
index fea9c116..00000000
--- a/fantas-eel-and-specification/tsconfig.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "compilerOptions": {
-    "noEmit": true,
-    "target": "es5",
-    "module": "commonjs",
-    "moduleResolution": "node",
-    "strict": true,
-    "noImplicitReturns": false,
-    "noUnusedLocals": true,
-    "noUnusedParameters": false,
-    "noFallthroughCasesInSwitch": true,
-    "forceConsistentCasingInFileNames": true,
-    "lib": ["es6", "dom"]
-  }
-}
diff --git a/package-lock.json b/package-lock.json
index aeba2cac..275088fb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6376,9 +6376,9 @@
       }
     },
     "typescript": {
-      "version": "2.9.1",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.1.tgz",
-      "integrity": "sha512-h6pM2f/GDchCFlldnriOhs1QHuwbnmj6/v7499eMHqPeW4V2G0elua2eIc2nu8v2NdHV0Gm+tzX83Hr6nUFjQA==",
+      "version": "2.9.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
+      "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
       "dev": true
     },
     "typings-checker": {
diff --git a/package.json b/package.json
index fdc7ff75..21701bc3 100644
--- a/package.json
+++ b/package.json
@@ -8,11 +8,11 @@
   "main": "lib/index.js",
   "typings": "lib/index.d.ts",
   "scripts": {
-    "lint": "tslint -p tsconfig.json src/**/*.ts test/**/*.ts examples/**/*.ts exercises/**/*.ts fantas-eel-and-specification/**/*.ts",
+    "lint": "tslint -p tsconfig.tslint.json src/**/*.ts test/**/*.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/*",
@@ -45,7 +45,7 @@
     "ts-simple-ast": "0.74.0",
     "tslint": "5.9.1",
     "tslint-config-standard": "7.0.0",
-    "typescript": "2.9.1",
+    "typescript": "^2.9.2",
     "typings-checker": "2.0.0"
   },
   "tags": [
diff --git a/src/Array.ts b/src/Array.ts
index a19b4d20..f4988563 100644
--- a/src/Array.ts
+++ b/src/Array.ts
@@ -14,7 +14,10 @@ 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'
+import { Witherable1 } from './Witherable'
 
 // Adapted from https://github.com/purescript/purescript-arrays
 
@@ -179,25 +182,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]
@@ -520,23 +504,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
@@ -634,33 +601,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
@@ -802,15 +742,128 @@ export const sortBy1 = <A>(head: Ord<A>, tail: Array<Ord<A>>): Endomorphism<Arra
   return sort(tail.reduce(getSemigroup<A>().concat, head))
 }
 
+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
+
+const wither = <F>(F: Applicative<F>): (<A, B>(ta: A[], f: (a: A) => HKT<F, Option<B>>) => HKT<F, B[]>) => {
+  const traverseF = traverse(F)
+  return (wa, f) => F.map(traverseF(wa, f), compact)
+}
+
+const wilt = <F>(
+  F: Applicative<F>
+): (<RL, RR, A>(wa: A[], f: (a: A) => HKT<F, Either<RL, RR>>) => HKT<F, Separated<RL[], RR[]>>) => {
+  const traverseF = traverse(F)
+  return (wa, f) => F.map(traverseF(wa, f), separate)
+}
+
 export const array: Monad1<URI> &
   Foldable1<URI> &
   Unfoldable1<URI> &
   Traversable1<URI> &
   Alternative1<URI> &
   Plus1<URI> &
-  Extend1<URI> = {
+  Extend1<URI> &
+  Compactable1<URI> &
+  Filterable1<URI> &
+  Witherable1<URI> = {
   URI,
   map,
+  compact,
+  separate,
+  filter,
+  filterMap,
+  partition,
+  partitionMap,
   of,
   ap,
   chain,
@@ -819,5 +872,7 @@ export const array: Monad1<URI> &
   traverse,
   zero,
   alt,
-  extend
+  extend,
+  wither,
+  wilt
 }
diff --git a/src/Compactable.ts b/src/Compactable.ts
new file mode 100644
index 00000000..a5e6e894
--- /dev/null
+++ b/src/Compactable.ts
@@ -0,0 +1,88 @@
+import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
+import { Option } from './Option'
+import { Either } from './Either'
+
+/**
+ * `Separated` type which holds `left` and `right` parts
+ * @since 1.7.0
+ */
+export type Separated<A, B> = {
+  readonly left: A
+  readonly right: B
+}
+
+/**
+ * @typeclass
+ * `Compactable` represents data structures which can be _compacted_/_filtered_.
+ * This is a generalization of catOptions as a new function `compact`. `compact` has relations with {@link Functor}, {@link Applicative}, {@link Monad}, {@link Plus}, and {@link Traversable} in that we can use these classes to provide the ability to operate on a data type by eliminating intermediate Nones. This is useful for representing the filtering out of values, or failure.
+ * - {@link compact}
+ * - {@link separate}
+ * @since 1.7.0
+ * @see https://github.com/LiamGoodacre/purescript-filterable/blob/master/src/Data/Compactable.purs
+ */
+export interface Compactable<F> {
+  readonly URI: F
+  /**
+   * Compacts data structure unwrapping inner {@link Option}
+   * @since 1.7.0
+   */
+  readonly compact: <A>(fa: HKT<F, Option<A>>) => HKT<F, A>
+  /**
+   * Separates data structure moving inner {@link Left} to the left side and inner {@link Right} to the right side of {@link Separated}
+   * @since 1.7.0
+   */
+  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 Compactable1<F extends URIS> {
+  readonly URI: F
+  readonly compact: <A>(fa: Type<F, Option<A>>) => Type<F, A>
+  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 Compactable2<F extends URIS2> {
+  readonly URI: F
+  readonly compact: <L, A>(fa: Type2<F, L, Option<A>>) => Type2<F, L, A>
+  readonly separate: <L, A, B>(fa: Type2<F, L, Either<A, B>>) => Separated<Type2<F, L, A>, Type2<F, L, B>>
+}
+
+/**
+ * @see Compactable
+ * @since 1.7.0
+ */
+export interface Compactable2C<F extends URIS2, L> {
+  readonly URI: F
+  readonly _L: L
+  readonly compact: <A>(fa: Type2<F, L, Option<A>>) => Type2<F, L, A>
+  readonly separate: <A, B>(fa: Type2<F, L, Either<A, B>>) => Separated<Type2<F, L, A>, Type2<F, L, B>>
+}
+
+/**
+ * @see Compactable
+ * @since 1.7.0
+ */
+export interface Compactable3<F extends URIS3> {
+  readonly URI: F
+  readonly compact: <U, L, A>(fa: Type3<F, U, L, Option<A>>) => Type3<F, U, L, A>
+  readonly separate: <U, L, A, B>(fa: Type3<F, U, L, Either<A, B>>) => Separated<Type3<F, U, L, A>, Type3<F, U, L, B>>
+}
+
+/**
+ * @see Compactable
+ * @since 1.7.0
+ */
+export interface Compactable3C<F extends URIS3, U, L> {
+  readonly URI: F
+  readonly _L: L
+  readonly _U: U
+  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>>
+}
diff --git a/src/Console.ts b/src/Console.ts
index 61c6ef78..2a521247 100644
--- a/src/Console.ts
+++ b/src/Console.ts
@@ -7,7 +7,7 @@ import { IO } from './IO'
  * @since 1.0.0
  */
 export const log = (s: any): IO<void> => {
-  return new IO(() => console.log(s))
+  return new IO(() => console.log(s)) // tslint:disable-line:no-console
 }
 
 /**
@@ -15,7 +15,7 @@ export const log = (s: any): IO<void> => {
  * @since 1.0.0
  */
 export const warn = (s: any): IO<void> => {
-  return new IO(() => console.warn(s))
+  return new IO(() => console.warn(s)) // tslint:disable-line:no-console
 }
 
 /**
@@ -23,7 +23,7 @@ export const warn = (s: any): IO<void> => {
  * @since 1.0.0
  */
 export const error = (s: any): IO<void> => {
-  return new IO(() => console.error(s))
+  return new IO(() => console.error(s)) // tslint:disable-line:no-console
 }
 
 /**
@@ -31,5 +31,5 @@ export const error = (s: any): IO<void> => {
  * @since 1.0.0
  */
 export const info = (s: any): IO<void> => {
-  return new IO(() => console.info(s))
+  return new IO(() => console.info(s)) // tslint:disable-line:no-console
 }
diff --git a/src/Const.ts b/src/Const.ts
index f6738a32..f1d75f1b 100644
--- a/src/Const.ts
+++ b/src/Const.ts
@@ -77,7 +77,7 @@ export const getApply = <L>(S: Semigroup<L>): Apply2C<URI, L> => {
   }
 }
 
-const of = <L>(M: Monoid<L>) => <A>(b: A): Const<L, A> => {
+const of = <L>(M: Monoid<L>) => <A>(a: A): Const<L, A> => {
   return new Const(M.empty)
 }
 
diff --git a/src/Either.ts b/src/Either.ts
index 5b266454..ca58eddb 100644
--- a/src/Either.ts
+++ b/src/Either.ts
@@ -4,13 +4,17 @@ import { Bifunctor2 } from './Bifunctor'
 import { ChainRec2, tailRec } from './ChainRec'
 import { Extend2 } from './Extend'
 import { Foldable2 } from './Foldable'
-import { Lazy, Predicate, Refinement, toString } from './function'
+import { Lazy, phantom, Predicate, Refinement, toString } from './function'
 import { HKT } from './HKT'
 import { Monad2 } from './Monad'
 import { Option } from './Option'
 import { Setoid } from './Setoid'
 import { Traversable2 } from './Traversable'
 import { Validation } from './Validation'
+import { Compactable2C, Separated } from './Compactable'
+import { Monoid } from './Monoid'
+import { Filterable2C } from './Filterable'
+import { Witherable2C } from './Witherable'
 
 declare module './HKT' {
   interface URI2HKT2<L, A> {
@@ -55,6 +59,9 @@ export type URI = typeof URI
  */
 export type Either<L, A> = Left<L, A> | Right<L, A>
 
+/**
+ * Left side of {@link Either}
+ */
 export class Left<L, A> {
   readonly _tag: 'Left' = 'Left'
   readonly _A!: A
@@ -170,6 +177,9 @@ export class Left<L, A> {
   }
 }
 
+/**
+ * Right side of {@link Either}
+ */
 export class Right<L, A> {
   readonly _tag: 'Right' = 'Right'
   readonly _A!: A
@@ -422,6 +432,150 @@ export const isRight = <L, A>(fa: Either<L, A>): fa is Right<L, A> => {
   return fa.isRight()
 }
 
+/**
+ * Builds {@link Compactable} instance for {@link Either} given {@link Monoid} for the left side
+ * @function
+ * @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> => {
+    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,
+    _L: phantom,
+    compact,
+    separate
+  }
+}
+
+/**
+ * Builds {@link Filterable} instance for {@link Either} given {@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
+  }
+}
+
+/**
+ * Builds {@link Witherable} instance for {@link Either} given {@link Monoid} for the left side
+ * @function
+ * @since 1.7.0
+ */
+export function getWitherable<L>(ML: Monoid<L>): Witherable2C<URI, L> {
+  const filterableEither = getFilterable(ML)
+
+  const wither = <F>(
+    F: Applicative<F>
+  ): (<A, B>(wa: Either<L, A>, f: (a: A) => HKT<F, Option<B>>) => HKT<F, Either<L, B>>) => {
+    const traverseF = traverse(F)
+    return (wa, f) => F.map(traverseF(wa, f), filterableEither.compact)
+  }
+
+  const wilt = <F>(
+    F: Applicative<F>
+  ): (<RL, RR, A>(
+    wa: Either<L, A>,
+    f: (a: A) => HKT<F, Either<RL, RR>>
+  ) => HKT<F, Separated<Either<L, RL>, Either<L, RR>>>) => {
+    const traverseF = traverse(F)
+    return (wa, f) => F.map(traverseF(wa, f), filterableEither.separate)
+  }
+
+  return {
+    ...filterableEither,
+    traverse,
+    reduce,
+    wither,
+    wilt
+  }
+}
+
 /**
  * @instance
  * @since 1.0.0
diff --git a/src/Filterable.ts b/src/Filterable.ts
new file mode 100644
index 00000000..10773a68
--- /dev/null
+++ b/src/Filterable.ts
@@ -0,0 +1,118 @@
+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 } from './Either'
+import { Predicate } from './function'
+import { Option } 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>
+}
diff --git a/src/IOEither.ts b/src/IOEither.ts
index cd77ee46..175a3ec1 100644
--- a/src/IOEither.ts
+++ b/src/IOEither.ts
@@ -125,7 +125,7 @@ const chain = <L, A, B>(fa: IOEither<L, A>, f: (a: A) => IOEither<L, B>): IOEith
   return fa.chain(f)
 }
 
-const alt = <L, A, B>(fx: IOEither<L, A>, fy: IOEither<L, A>): IOEither<L, A> => {
+const alt = <L, A>(fx: IOEither<L, A>, fy: IOEither<L, A>): IOEither<L, A> => {
   return fx.alt(fy)
 }
 
diff --git a/src/Monoid.ts b/src/Monoid.ts
index 711d31ea..ec86f422 100644
--- a/src/Monoid.ts
+++ b/src/Monoid.ts
@@ -171,13 +171,16 @@ export const getEndomorphismMonoid = <A = never>(): Monoid<Endomorphism<A>> => {
  * @function
  * @since 1.0.0
  */
-export const getRecordMonoid = <O>(Ms: { [K in keyof O]: Monoid<O[K]> }): Monoid<O> => {
+export const getRecordMonoid = <O extends { [key: string]: any }>(
+  monoids: { [K in keyof O]: Monoid<O[K]> }
+): Monoid<O> => {
   const empty: any = {}
-  for (const k in Ms) {
-    empty[k] = Ms[k].empty
+  const keys = Object.keys(monoids)
+  for (const key of keys) {
+    empty[key] = monoids[key].empty
   }
   return {
-    ...getRecordSemigroup<O>(Ms),
+    ...getRecordSemigroup<O>(monoids),
     empty
   }
 }
diff --git a/src/Option.ts b/src/Option.ts
index ee9178ca..3c9e7a0f 100644
--- a/src/Option.ts
+++ b/src/Option.ts
@@ -5,13 +5,16 @@ import { Extend1 } from './Extend'
 import { Foldable1 } from './Foldable'
 import { HKT } from './HKT'
 import { Monad1 } from './Monad'
-import { Monoid, getDualMonoid } from './Monoid'
+import { getDualMonoid, Monoid } from './Monoid'
 import { Ord } from './Ord'
 import { Plus1 } from './Plus'
 import { Semigroup } from './Semigroup'
 import { Setoid } from './Setoid'
 import { Traversable1 } from './Traversable'
-import { Lazy, Predicate, Refinement, toString } from './function'
+import { identity, Lazy, not, Predicate, Refinement, toString } from './function'
+import { Compactable1, Separated } from './Compactable'
+import { Filterable1 } from './Filterable'
+import { Witherable1 } from './Witherable'
 
 declare module './HKT' {
   interface URI2HKT<A> {
@@ -554,6 +557,62 @@ 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>> => {
+  if (fa.isNone()) {
+    return {
+      left: none,
+      right: none
+    }
+  }
+  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)
+})
+
+const wither = <F>(F: Applicative<F>) => <A, B>(fa: Option<A>, f: (a: A) => HKT<F, Option<B>>): HKT<F, Option<B>> =>
+  fa.isNone() ? F.of(fa as any) : f(fa.value)
+
+const wilt = <F>(F: Applicative<F>) => <RL, RR, A>(
+  fa: Option<A>,
+  f: (a: A) => HKT<F, Either<RL, RR>>
+): HKT<F, Separated<Option<RL>, Option<RR>>> => {
+  if (fa.isNone()) {
+    return F.of({
+      left: none,
+      right: none
+    })
+  }
+  return F.map(f(fa.value), e => {
+    if (e.isLeft()) {
+      return {
+        left: some(e.value),
+        right: none
+      }
+    }
+    return {
+      left: none,
+      right: some(e.value)
+    }
+  })
+}
 
 /**
  * @instance
@@ -564,7 +623,10 @@ export const option: Monad1<URI> &
   Plus1<URI> &
   Traversable1<URI> &
   Alternative1<URI> &
-  Extend1<URI> = {
+  Extend1<URI> &
+  Compactable1<URI> &
+  Filterable1<URI> &
+  Witherable1<URI> = {
   URI,
   map,
   of,
@@ -574,5 +636,13 @@ export const option: Monad1<URI> &
   traverse,
   zero,
   alt,
-  extend
+  extend,
+  compact,
+  separate,
+  filter,
+  filterMap,
+  partition,
+  partitionMap,
+  wither,
+  wilt
 }
diff --git a/src/Semigroup.ts b/src/Semigroup.ts
index 88f28111..2592a91d 100644
--- a/src/Semigroup.ts
+++ b/src/Semigroup.ts
@@ -70,8 +70,9 @@ export const getRecordSemigroup = <O extends { [key: string]: any }>(
   return {
     concat: (x, y) => {
       const r: any = {}
-      for (const k in semigroups) {
-        r[k] = semigroups[k].concat(x[k], y[k])
+      const keys = Object.keys(semigroups)
+      for (const key of keys) {
+        r[key] = semigroups[key].concat(x[key], y[key])
       }
       return r
     }
diff --git a/src/Set.ts b/src/Set.ts
index 18be9707..6df767a1 100644
--- a/src/Set.ts
+++ b/src/Set.ts
@@ -4,6 +4,7 @@ import { Ord } from './Ord'
 import { Semigroup } from './Semigroup'
 import { Setoid } from './Setoid'
 import { Predicate, not } from './function'
+import { Separated } from './Compactable'
 
 /**
  * @function
@@ -114,7 +115,7 @@ export const filter = <A>(x: Set<A>, predicate: Predicate<A>): Set<A> => {
  * @function
  * @since 1.2.0
  */
-export const partition = <A>(x: Set<A>, predicate: Predicate<A>): { right: Set<A>; left: Set<A> } => {
+export const partition = <A>(x: Set<A>, predicate: Predicate<A>): Separated<Set<A>, Set<A>> => {
   const values = x.values()
   let e: IteratorResult<A>
   let t = new Set()
@@ -182,7 +183,7 @@ export const intersection = <A>(S: Setoid<A>): ((x: Set<A>, y: Set<A>) => Set<A>
  * @function
  * @since 1.2.0
  */
-export const partitionMap = <A, L, R>(x: Set<A>, f: (a: A) => Either<L, R>): { left: Set<L>; right: Set<R> } => {
+export const partitionMap = <A, L, R>(x: Set<A>, f: (a: A) => Either<L, R>): Separated<Set<L>, Set<R>> => {
   const values = x.values()
   let e: IteratorResult<A>
   let l = new Set()
diff --git a/src/State.ts b/src/State.ts
index 34576f7e..e2159925 100644
--- a/src/State.ts
+++ b/src/State.ts
@@ -1,3 +1,4 @@
+import { constant, constIdentity } from './function'
 import { Monad2 } from './Monad'
 
 declare module './HKT' {
@@ -38,6 +39,20 @@ export class State<S, A> {
   ap_<B, C>(this: State<S, (b: B) => C>, fb: State<S, B>): State<S, C> {
     return fb.ap(this)
   }
+  /**
+   * Combine two effectful actions, keeping only the result of the first
+   * @since 1.7.0
+   */
+  applyFirst<B>(fb: State<S, B>): State<S, A> {
+    return fb.ap(this.map(constant))
+  }
+  /**
+   * Combine two effectful actions, keeping only the result of the second
+   * @since 1.7.0
+   */
+  applySecond<B>(fb: State<S, B>): State<S, B> {
+    return fb.ap(this.map(constIdentity as () => (b: B) => B))
+  }
   chain<B>(f: (a: A) => State<S, B>): State<S, B> {
     return new State(s => {
       const [a, s1] = this.run(s)
diff --git a/src/StrMap.ts b/src/StrMap.ts
index 7594dd81..cc32b948 100644
--- a/src/StrMap.ts
+++ b/src/StrMap.ts
@@ -10,6 +10,10 @@ import { Setoid } from './Setoid'
 import { Traversable1 } from './Traversable'
 import { Unfoldable } from './Unfoldable'
 import { Predicate, tuple } from './function'
+import { Either } from './Either'
+import { Compactable1, Separated } from './Compactable'
+import { Filterable1 } from './Filterable'
+import { Witherable1 } from './Witherable'
 
 // https://github.com/purescript/purescript-maps
 
@@ -33,10 +37,11 @@ export class StrMap<A> {
   readonly _URI!: URI
   constructor(readonly value: { [key: string]: A }) {}
   mapWithKey<B>(f: (k: string, a: A) => B): StrMap<B> {
-    const o = this.value
+    const value = this.value
     const r: { [key: string]: B } = {}
-    for (let k in o) {
-      r[k] = f(k, o[k])
+    const keys = Object.keys(value)
+    for (const key of keys) {
+      r[key] = f(key, value[key])
     }
     return new StrMap(r)
   }
@@ -57,15 +62,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)
   }
 }
 
@@ -124,8 +121,10 @@ export function traverseWithKey<F>(
       concatCurried(getLastSemigroup())
     )
     let out: HKT<F, StrMap<B>> = F.of(empty)
-    for (let k in ta.value) {
-      out = concatA2(out)(F.map(f(k, ta.value[k]), b => singleton(k, b)))
+    const value = ta.value
+    const keys = Object.keys(value)
+    for (const key of keys) {
+      out = concatA2(out)(F.map(f(key, value[key]), b => singleton(key, b)))
     }
     return out
   }
@@ -164,10 +163,7 @@ export const size = <A>(d: StrMap<A>): number => {
  * @since 1.0.0
  */
 export const isEmpty = <A>(d: StrMap<A>): boolean => {
-  for (const _ in d.value) {
-    return false
-  }
-  return true
+  return Object.keys(d.value).length === 0
 }
 
 /**
@@ -236,8 +232,10 @@ export function fromFoldable<F>(
  */
 export const collect = <A, B>(d: StrMap<A>, f: (k: string, a: A) => B): Array<B> => {
   const out: Array<B> = []
-  for (let k in d.value) {
-    out.push(f(k, d.value[k]))
+  const value = d.value
+  const keys = Object.keys(value)
+  for (const key of keys) {
+    out.push(f(key, d.value[key]))
   }
   return out
 }
@@ -293,13 +291,133 @@ 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 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 (const key of keys) {
+    const optionA = value[key]
+    if (optionA.isSome()) {
+      result[key] = optionA.value
+    }
+  }
+  return new StrMap(result)
+}
+
+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 (const key of keys) {
+    const e = value[key]
+    if (e.isLeft()) {
+      left[key] = e.value
+    } else {
+      right[key] = e.value
+    }
+  }
+  return {
+    left: new StrMap(left),
+    right: new StrMap(right)
+  }
+}
+
+const wither = <F>(F: Applicative<F>): (<A, B>(wa: StrMap<A>, f: (a: A) => HKT<F, Option<B>>) => HKT<F, StrMap<B>>) => {
+  const traverseF = traverse(F)
+  return (wa, f) => F.map(traverseF(wa, f), compact)
+}
+
+const wilt = <F>(
+  F: Applicative<F>
+): (<RL, RR, A>(wa: StrMap<A>, f: (a: A) => HKT<F, Either<RL, RR>>) => HKT<F, Separated<StrMap<RL>, StrMap<RR>>>) => {
+  const traverseF = traverse(F)
+  return (wa, f) => F.map(traverseF(wa, f), separate)
+}
 /**
  * @instance
  * @since 1.0.0
  */
-export const strmap: Functor1<URI> & Foldable1<URI> & Traversable1<URI> = {
+export const strmap: Functor1<URI> &
+  Foldable1<URI> &
+  Traversable1<URI> &
+  Compactable1<URI> &
+  Filterable1<URI> &
+  Witherable1<URI> = {
   URI,
   map,
   reduce,
-  traverse
+  traverse,
+  compact,
+  separate,
+  filter,
+  filterMap,
+  partition,
+  partitionMap,
+  wither,
+  wilt
 }
diff --git a/src/Trace.ts b/src/Trace.ts
index c9f83dcc..c616008f 100644
--- a/src/Trace.ts
+++ b/src/Trace.ts
@@ -12,7 +12,7 @@ import { Lazy } from './function'
  * @since 1.0.0
  */
 export const trace = <A>(message: any, out: Lazy<A>): A => {
-  console.log(message)
+  console.log(message) // tslint:disable-line:no-console
   return out()
 }
 
diff --git a/src/Traversable.ts b/src/Traversable.ts
index 4031e9d0..4c42c3ad 100644
--- a/src/Traversable.ts
+++ b/src/Traversable.ts
@@ -21,39 +21,216 @@ import {
   FunctorComposition11,
   getFunctorComposition
 } from './Functor'
-import { HKT, HKT2, HKT3, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
+import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
 
-/** @typeclass */
+/**
+ * Interface for {@link Traversable.traverse}
+ * @since 1.7.0
+ */
+export interface Traverse<T> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, A, B>(
+    ta: HKT<T, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, HKT<T, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <A, B>(
+    ta: HKT<T, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, HKT<T, B>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, A, B>(
+    ta: HKT<T, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, HKT<T, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <A, B>(
+    ta: HKT<T, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, HKT<T, B>>
+  <F extends URIS>(F: Applicative1<F>): <A, B>(ta: HKT<T, A>, f: (a: A) => Type<F, B>) => Type<F, HKT<T, B>>
+  <F>(F: Applicative<F>): <A, B>(ta: HKT<T, A>, f: (a: A) => HKT<F, B>) => HKT<F, HKT<T, B>>
+}
+
+/**
+ * Interface for {@link Traversable1.traverse}
+ * @see Traverse
+ * @since 1.7.0
+ */
+export interface Traverse1<T extends URIS> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, A, B>(
+    ta: Type<T, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, Type<T, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <A, B>(
+    ta: Type<T, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, Type<T, B>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, A, B>(
+    ta: Type<T, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, Type<T, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <A, B>(
+    ta: Type<T, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, Type<T, B>>
+  <F extends URIS>(F: Applicative1<F>): <A, B>(ta: Type<T, A>, f: (a: A) => Type<F, B>) => Type<F, Type<T, B>>
+  <F>(F: Applicative<F>): <A, B>(ta: Type<T, A>, f: (a: A) => HKT<F, B>) => HKT<F, Type<T, B>>
+}
+
+/**
+ * Interface for {@link Traversable2.traverse}
+ * @see Traverse
+ * @since 1.7.0
+ */
+export interface Traverse2<T extends URIS2> {
+  <F extends URIS3>(F: Applicative3<F>): <TL, FU, FL, A, B>(
+    ta: Type2<T, TL, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, Type2<T, TL, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <TL, A, B>(
+    ta: Type2<T, TL, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, Type2<T, TL, B>>
+  <F extends URIS2>(F: Applicative2<F>): <TL, FL, A, B>(
+    ta: Type2<T, TL, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, Type2<T, TL, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <TL, A, B>(
+    ta: Type2<T, TL, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, Type2<T, TL, B>>
+  <F extends URIS>(F: Applicative1<F>): <TL, A, B>(
+    ta: Type2<T, TL, A>,
+    f: (a: A) => Type<F, B>
+  ) => Type<F, Type2<T, TL, B>>
+  <F>(F: Applicative<F>): <TL, A, B>(ta: Type2<T, TL, A>, f: (a: A) => HKT<F, B>) => HKT<F, Type2<T, TL, B>>
+}
+
+/**
+ * Interface for {@link Traversable2C.traverse}
+ * @see Traverse
+ * @since 1.7.0
+ */
+export interface Traverse2C<T extends URIS2, TL> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, A, B>(
+    ta: Type2<T, TL, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, Type2<T, TL, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <A, B>(
+    ta: Type2<T, TL, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, Type2<T, TL, B>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, A, B>(
+    ta: Type2<T, TL, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, Type2<T, TL, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <A, B>(
+    ta: Type2<T, TL, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, Type2<T, TL, B>>
+  <F extends URIS>(F: Applicative1<F>): <A, B>(ta: Type2<T, TL, A>, f: (a: A) => Type<F, B>) => Type<F, Type2<T, TL, B>>
+  <F>(F: Applicative<F>): <A, B>(ta: Type2<T, TL, A>, f: (a: A) => HKT<F, B>) => HKT<F, Type2<T, TL, B>>
+}
+
+/**
+ * Interface for {@link Traversable3.traverse}
+ * @see Traverse
+ * @since 1.7.0
+ */
+export interface Traverse3<T extends URIS3> {
+  <F extends URIS3>(F: Applicative3<F>): <TU, TL, FU, FL, A, B>(
+    ta: Type3<T, TU, TL, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, Type3<T, TU, TL, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <TU, TL, A, B>(
+    ta: Type3<T, TU, TL, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, Type3<T, TU, TL, B>>
+  <F extends URIS2>(F: Applicative2<F>): <TU, TL, FL, A, B>(
+    ta: Type3<T, TU, TL, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, Type3<T, TU, TL, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <TU, TL, A, B>(
+    ta: Type3<T, TU, TL, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, Type3<T, TU, TL, B>>
+  <F extends URIS>(F: Applicative1<F>): <TU, TL, A, B>(
+    ta: Type3<T, TU, TL, A>,
+    f: (a: A) => Type<F, B>
+  ) => Type<F, Type3<T, TU, TL, B>>
+  <F>(F: Applicative<F>): <TU, TL, A, B>(ta: Type3<T, TU, TL, A>, f: (a: A) => HKT<F, B>) => HKT<F, Type3<T, TU, TL, B>>
+}
+
+/**
+ * Interface for {@link Traversable3C.traverse}
+ * @see Traverse
+ * @since 1.7.0
+ */
+export interface Traverse3C<T extends URIS3, TU, TL> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, A, B>(
+    ta: Type3<T, TU, TL, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, Type3<T, TU, TL, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <A, B>(
+    ta: Type3<T, TU, TL, A>,
+    f: (a: A) => Type3<F, FU, FL, B>
+  ) => Type3<F, FU, FL, Type3<T, TU, TL, B>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, A, B>(
+    ta: Type3<T, TU, TL, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, Type3<T, TU, TL, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <A, B>(
+    ta: Type3<T, TU, TL, A>,
+    f: (a: A) => Type2<F, FL, B>
+  ) => Type2<F, FL, Type3<T, TU, TL, B>>
+  <F extends URIS>(F: Applicative1<F>): <A, B>(
+    ta: Type3<T, TU, TL, A>,
+    f: (a: A) => Type<F, B>
+  ) => Type<F, Type3<T, TU, TL, B>>
+  <F>(F: Applicative<F>): <A, B>(ta: Type3<T, TU, TL, A>, f: (a: A) => HKT<F, B>) => HKT<F, Type3<T, TU, TL, B>>
+}
+
+/**
+ * @typeclass
+ * `Traversable` represents data structures which can be _traversed_ accumulating results and effects in some {@link Applicative} functor.
+ */
 export interface Traversable<T> extends Functor<T>, Foldable<T> {
-  readonly traverse: <F>(F: Applicative<F>) => <A, B>(ta: HKT<T, A>, f: (a: A) => HKT<F, B>) => HKT<F, HKT<T, B>>
+  /**
+   * Runs an action for every element in a data structure and accumulates the results
+   */
+  traverse: Traverse<T>
 }
 
+/**
+ * @see Traversable
+ */
 export interface Traversable1<T extends URIS> extends Functor1<T>, Foldable1<T> {
-  readonly traverse: <F>(F: Applicative<F>) => <A, B>(ta: Type<T, A>, f: (a: A) => HKT<F, B>) => HKT<F, Type<T, B>>
+  traverse: Traverse1<T>
 }
 
+/**
+ * @see Traversable
+ */
 export interface Traversable2<T extends URIS2> extends Functor2<T>, Foldable2<T> {
-  readonly traverse: <F>(
-    F: Applicative<F>
-  ) => <L, A, B>(ta: Type2<T, L, A>, f: (a: A) => HKT<F, B>) => HKT<F, Type2<T, L, B>>
+  traverse: Traverse2<T>
 }
 
-export interface Traversable3<T extends URIS3> extends Functor3<T>, Foldable3<T> {
-  readonly traverse: <F>(
-    F: Applicative<F>
-  ) => <U, L, A, B>(ta: Type3<T, U, L, A>, f: (a: A) => HKT<F, B>) => HKT<F, Type3<T, U, L, B>>
+/**
+ * @see Traversable
+ */
+export interface Traversable2C<T extends URIS2, TL> extends Functor2C<T, TL>, Foldable2C<T, TL> {
+  traverse: Traverse2C<T, TL>
 }
 
-export interface Traversable2C<T extends URIS2, L> extends Functor2C<T, L>, Foldable2C<T, L> {
-  readonly traverse: <F>(
-    F: Applicative<F>
-  ) => <A, B>(ta: HKT2<T, L, A>, f: (a: A) => HKT<F, B>) => HKT<F, Type2<T, L, B>>
+/**
+ * @see Traversable
+ */
+export interface Traversable3<T extends URIS3> extends Functor3<T>, Foldable3<T> {
+  traverse: Traverse3<T>
 }
 
-export interface Traversable3C<T extends URIS3, U, L> extends Functor3C<T, U, L>, Foldable3C<T, U, L> {
-  readonly traverse: <F>(
-    F: Applicative<F>
-  ) => <A, B>(ta: HKT3<T, U, L, A>, f: (a: A) => HKT<F, B>) => HKT<F, Type3<T, U, L, B>>
+/**
+ * @see Traversable
+ */
+export interface Traversable3C<T extends URIS3, TU, TL> extends Functor3C<T, TU, TL>, Foldable3C<T, TU, TL> {
+  traverse: Traverse3C<T, TU, TL>
 }
 
 export interface TraversableComposition<F, G> extends FoldableComposition<F, G>, FunctorComposition<F, G> {
@@ -78,6 +255,10 @@ export function traverse<F extends URIS2, T extends URIS2>(
   F: Applicative2<F>,
   T: Traversable2<T>
 ): <LF, LT, A, B>(ta: Type2<T, LT, A>, f: (a: A) => Type2<F, LF, B>) => Type2<F, LF, Type2<T, LT, B>>
+export function traverse<F extends URIS2, T extends URIS2, LF>(
+  F: Applicative2C<F, LF>,
+  T: Traversable2<T>
+): <LT, A, B>(ta: Type2<T, LT, A>, f: (a: A) => Type2<F, LF, B>) => Type2<F, LF, Type2<T, LT, B>>
 export function traverse<F extends URIS, T extends URIS2>(
   F: Applicative1<F>,
   T: Traversable2<T>
@@ -90,6 +271,10 @@ export function traverse<F extends URIS2, T extends URIS>(
   F: Applicative2<F>,
   T: Traversable1<T>
 ): <L, A, B>(ta: Type<T, A>, f: (a: A) => Type2<F, L, B>) => Type2<F, L, Type<T, B>>
+export function traverse<F extends URIS2, T extends URIS, L>(
+  F: Applicative2C<F, L>,
+  T: Traversable1<T>
+): <A, B>(ta: Type<T, A>, f: (a: A) => Type2<F, L, B>) => Type2<F, L, Type<T, B>>
 export function traverse<F extends URIS, T extends URIS>(
   F: Applicative1<F>,
   T: Traversable1<T>
@@ -99,6 +284,8 @@ export function traverse<F, T>(
   T: Traversable<T>
 ): <A, B>(ta: HKT<T, A>, f: (a: A) => HKT<F, B>) => HKT<F, HKT<T, B>>
 /**
+ * @deprecated
+ * Use {@link Traversable.traverse}
  * @function
  * @since 1.0.0
  */
diff --git a/src/Validation.ts b/src/Validation.ts
index 64c3c37f..61dacad3 100644
--- a/src/Validation.ts
+++ b/src/Validation.ts
@@ -11,6 +11,10 @@ import { Setoid } from './Setoid'
 import { Traversable2 } from './Traversable'
 import { Predicate, phantom, toString } from './function'
 import { Bifunctor2 } from './Bifunctor'
+import { Compactable2C, Separated } from './Compactable'
+import { Option } from './Option'
+import { Filterable2C } from './Filterable'
+import { Witherable2C } from './Witherable'
 
 // Adapted from https://github.com/purescript/purescript-validation
 
@@ -29,6 +33,44 @@ export type URI = typeof URI
  *
  * The `Applicative` instance collects multiple failures in an arbitrary `Semigroup`.
  *
+ * @example
+ *
+ * import { Validation, getApplicative, success, failure } from 'fp-ts/lib/Validation'
+ * import { NonEmptyArray, getSemigroup } from 'fp-ts/lib/NonEmptyArray'
+ *
+ * interface Person {
+ *   readonly name: string
+ *   readonly age: number
+ * }
+ *
+ * // curried constructor
+ * const person = (name: string) => (age: number): Person => ({ name, age })
+ *
+ * // validators
+ * function validateName(input: string): Validation<NonEmptyArray<string>, string> {
+ *   return input.length === 0 ? failure(new NonEmptyArray('Invalid name: empty string', [])) : success(input)
+ * }
+ * function validateAge(input: string): Validation<NonEmptyArray<string>, number> {
+ *   const n = parseFloat(input)
+ *   if (isNaN(n)) {
+ *     return failure(new NonEmptyArray(`Invalid age: not a number ${input}`, []))
+ *   }
+ *   return n % 1 !== 0 ? failure(new NonEmptyArray(`Invalid age: not an integer ${n}`, [])) : success(n)
+ * }
+ *
+ * // get an `Applicative` instance for Validation<NonEmptyArray<string>, ?>
+ * const A = getApplicative(getSemigroup<string>())
+ *
+ * function validatePerson(input: Record<string, string>): Validation<NonEmptyArray<string>, Person> {
+ *   return A.ap(validateName(input['name']).map(person), validateAge(input['age']))
+ * }
+ *
+ * console.log(validatePerson({ name: '', age: '1.2' }))
+ * // failure(new NonEmptyArray("Invalid name: empty string", ["Invalid age: not an integer 1.2"]))
+ *
+ * console.log(validatePerson({ name: 'Giulio', age: '44' }))
+ * // success({ "name": "Giulio", "age": 44 })
+ *
  * @data
  * @constructor Failure
  * @constructor Success
@@ -323,6 +365,159 @@ export const isSuccess = <L, A>(fa: Validation<L, A>): fa is Success<L, A> => {
   return fa.isSuccess()
 }
 
+/**
+ * Builds {@link Compactable} instance for {@link Validation} given {@link Monoid} for the failure side
+ * @function
+ * @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> => {
+    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,
+    compact,
+    separate
+  }
+}
+
+/**
+ * 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
+  }
+}
+
+/**
+ * Builds {@link Witherable} instance for {@link Validation} given {@link Monoid} for the left side
+ * @function
+ * @since 1.7.0
+ */
+export function getWitherable<L>(ML: Monoid<L>): Witherable2C<URI, L> {
+  const filterableValidation = getFilterable(ML)
+
+  const wither = <F>(
+    F: Applicative<F>
+  ): (<A, B>(wa: Validation<L, A>, f: (a: A) => HKT<F, Option<B>>) => HKT<F, Validation<L, B>>) => {
+    const traverseF = traverse(F)
+    return (wa, f) => F.map(traverseF(wa, f), filterableValidation.compact)
+  }
+
+  const wilt = <F>(
+    F: Applicative<F>
+  ): (<RL, RR, A>(
+    wa: Validation<L, A>,
+    f: (a: A) => HKT<F, Either<RL, RR>>
+  ) => HKT<F, Separated<Validation<L, RL>, Validation<L, RR>>>) => {
+    const traverseF = traverse(F)
+    return (wa, f) => F.map(traverseF(wa, f), filterableValidation.separate)
+  }
+
+  return {
+    ...filterableValidation,
+    traverse,
+    reduce,
+    wither,
+    wilt
+  }
+}
+
 /**
  * @instance
  * @since 1.0.0
diff --git a/src/Witherable.ts b/src/Witherable.ts
new file mode 100644
index 00000000..42e37bb7
--- /dev/null
+++ b/src/Witherable.ts
@@ -0,0 +1,424 @@
+import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
+import { Option } from './Option'
+import { Traversable, Traversable1, Traversable2, Traversable2C, Traversable3, Traversable3C } from './Traversable'
+import { Applicative, Applicative1, Applicative2, Applicative2C, Applicative3, Applicative3C } from './Applicative'
+import { Filterable, Filterable1, Filterable2, Filterable2C, Filterable3, Filterable3C } from './Filterable'
+import { Either } from './Either'
+import { Separated } from './Compactable'
+
+/**
+ * Interface for {@link Witherable.wither}
+ * @since 1.7.0
+ */
+export interface Wither<W> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, A, B>(
+    ta: HKT<W, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, HKT<W, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <A, B>(
+    ta: HKT<W, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, HKT<W, B>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, A, B>(
+    ta: HKT<W, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, HKT<W, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <A, B>(
+    ta: HKT<W, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, HKT<W, B>>
+  <F extends URIS>(F: Applicative1<F>): <A, B>(ta: HKT<W, A>, f: (a: A) => Type<F, Option<B>>) => Type<F, HKT<W, B>>
+  <F>(F: Applicative<F>): <A, B>(ta: HKT<W, A>, f: (a: A) => HKT<F, Option<B>>) => HKT<F, HKT<W, B>>
+}
+
+/**
+ * @see Wither
+ * @since 1.7.0
+ */
+export interface Wither1<W extends URIS> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, A, B>(
+    ta: Type<W, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, Type<W, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <A, B>(
+    ta: Type<W, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, Type<W, B>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, A, B>(
+    ta: Type<W, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, Type<W, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <A, B>(
+    ta: Type<W, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, Type<W, B>>
+  <F extends URIS>(F: Applicative1<F>): <A, B>(ta: Type<W, A>, f: (a: A) => Type<F, Option<B>>) => Type<F, Type<W, B>>
+  <F>(F: Applicative<F>): <A, B>(ta: Type<W, A>, f: (a: A) => HKT<F, Option<B>>) => HKT<F, Type<W, B>>
+}
+
+/**
+ * @see Wither
+ * @since 1.7.0
+ */
+export interface Wither2<W extends URIS2> {
+  <F extends URIS3>(F: Applicative3<F>): <WL, FU, FL, A, B>(
+    ta: Type2<W, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, Type2<W, WL, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <WL, A, B>(
+    ta: Type2<W, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, Type2<W, WL, B>>
+  <F extends URIS2>(F: Applicative2<F>): <WL, FL, A, B>(
+    ta: Type2<W, WL, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, Type2<W, WL, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <WL, A, B>(
+    ta: Type2<W, WL, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, Type2<W, WL, B>>
+  <F extends URIS>(F: Applicative1<F>): <WL, A, B>(
+    ta: Type2<W, WL, A>,
+    f: (a: A) => Type<F, Option<B>>
+  ) => Type<F, Type2<W, WL, B>>
+  <F>(F: Applicative<F>): <WL, A, B>(ta: Type2<W, WL, A>, f: (a: A) => HKT<F, Option<B>>) => HKT<F, Type2<W, WL, B>>
+}
+
+/**
+ * @see Wither
+ * @since 1.7.0
+ */
+export interface Wither2C<W extends URIS2, WL> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, A, B>(
+    ta: Type2<W, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, Type2<W, WL, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <A, B>(
+    ta: Type2<W, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, Type2<W, WL, B>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, A, B>(
+    ta: Type2<W, WL, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, Type2<W, WL, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <A, B>(
+    ta: Type2<W, WL, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, Type2<W, WL, B>>
+  <F extends URIS>(F: Applicative1<F>): <A, B>(
+    ta: Type2<W, WL, A>,
+    f: (a: A) => Type<F, Option<B>>
+  ) => Type<F, Type2<W, WL, B>>
+  <F>(F: Applicative<F>): <A, B>(ta: Type2<W, WL, A>, f: (a: A) => HKT<F, Option<B>>) => HKT<F, Type2<W, WL, B>>
+}
+
+/**
+ * @see Wither
+ * @since 1.7.0
+ */
+export interface Wither3<W extends URIS3> {
+  <F extends URIS3>(F: Applicative3<F>): <WU, WL, FU, FL, A, B>(
+    ta: Type3<W, WU, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, Type3<W, WU, WL, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <WU, WL, A, B>(
+    ta: Type3<W, WU, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, Type3<W, WU, WL, B>>
+  <F extends URIS2>(F: Applicative2<F>): <WU, WL, FL, A, B>(
+    ta: Type3<W, WU, WL, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, Type3<W, WU, WL, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <WU, WL, A, B>(
+    ta: Type3<W, WU, WL, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, Type3<W, WU, WL, B>>
+  <F extends URIS>(F: Applicative1<F>): <WU, WL, A, B>(
+    ta: Type3<W, WU, WL, A>,
+    f: (a: A) => Type<F, Option<B>>
+  ) => Type<F, Type3<W, WU, WL, B>>
+  <F>(F: Applicative<F>): <WU, WL, A, B>(
+    ta: Type3<W, WU, WL, A>,
+    f: (a: A) => HKT<F, Option<B>>
+  ) => HKT<F, Type3<W, WU, WL, B>>
+}
+
+/**
+ * @see Wither
+ * @since 1.7.0
+ */
+export interface Wither3C<W extends URIS3, WU, WL> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, A, B>(
+    ta: Type3<W, WU, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, Type3<W, WU, WL, B>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <A, B>(
+    ta: Type3<W, WU, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Option<B>>
+  ) => Type3<F, FU, FL, Type3<W, WU, WL, B>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, A, B>(
+    ta: Type3<W, WU, WL, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, Type3<W, WU, WL, B>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <A, B>(
+    ta: Type3<W, WU, WL, A>,
+    f: (a: A) => Type2<F, FL, Option<B>>
+  ) => Type2<F, FL, Type3<W, WU, WL, B>>
+  <F extends URIS>(F: Applicative1<F>): <A, B>(
+    ta: Type3<W, WU, WL, A>,
+    f: (a: A) => Type<F, Option<B>>
+  ) => Type<F, Type3<W, WU, WL, B>>
+  <F>(F: Applicative<F>): <A, B>(ta: Type3<W, WU, WL, A>, f: (a: A) => HKT<F, Option<B>>) => HKT<F, Type3<W, WU, WL, B>>
+}
+
+/**
+ * Interface for {@link Witherable.wilt}
+ * @since 1.7.0
+ */
+export interface Wilt<W> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, RL, RR, A>(
+    wa: HKT<W, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, HKT<W, RR>>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <RL, RR, A>(
+    wa: HKT<W, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, HKT<W, RR>>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, RL, RR, A>(
+    wa: HKT<W, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, HKT<W, RR>>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <RL, RR, A>(
+    wa: HKT<W, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, HKT<W, RR>>>
+  <F extends URIS>(F: Applicative1<F>): <RL, RR, A>(
+    wa: HKT<W, A>,
+    f: (a: A) => Type<F, Either<RL, RR>>
+  ) => Type<F, Separated<HKT<W, RL>, HKT<W, RR>>>
+  <F>(F: Applicative<F>): <RL, RR, A>(
+    wa: HKT<W, A>,
+    f: (a: A) => HKT<F, Either<RL, RR>>
+  ) => HKT<F, Separated<HKT<W, RL>, HKT<W, RR>>>
+}
+
+/**
+ * @see Wilt
+ * @since 1.7.0
+ */
+export interface Wilt1<W extends URIS> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, RL, RR, A>(
+    wa: Type<W, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, Type<W, RR>>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <RL, RR, A>(
+    wa: Type<W, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, Type<W, RR>>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, RL, RR, A>(
+    wa: Type<W, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, Type<W, RR>>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <RL, RR, A>(
+    wa: Type<W, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, Type<W, RR>>>
+  <F extends URIS>(F: Applicative1<F>): <RL, RR, A>(
+    wa: Type<W, A>,
+    f: (a: A) => Type<F, Either<RL, RR>>
+  ) => Type<F, Separated<HKT<W, RL>, Type<W, RR>>>
+  <F>(F: Applicative<F>): <RL, RR, A>(
+    wa: Type<W, A>,
+    f: (a: A) => HKT<F, Either<RL, RR>>
+  ) => HKT<F, Separated<HKT<W, RL>, Type<W, RR>>>
+}
+
+/**
+ * @see Wilt
+ * @since 1.7.0
+ */
+export interface Wilt2<W extends URIS2> {
+  <F extends URIS3>(F: Applicative3<F>): <WL, FU, FL, RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <WL, RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+  <F extends URIS2>(F: Applicative2<F>): <WL, FL, RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <WL, RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+  <F extends URIS>(F: Applicative1<F>): <WL, RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => Type<F, Either<RL, RR>>
+  ) => Type<F, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+  <F>(F: Applicative<F>): <WL, RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => HKT<F, Either<RL, RR>>
+  ) => HKT<F, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+}
+
+/**
+ * @see Wilt
+ * @since 1.7.0
+ */
+export interface Wilt2C<W extends URIS2, WL> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+  <F extends URIS>(F: Applicative1<F>): <RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => Type<F, Either<RL, RR>>
+  ) => Type<F, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+  <F>(F: Applicative<F>): <RL, RR, A>(
+    wa: Type2<W, WL, A>,
+    f: (a: A) => HKT<F, Either<RL, RR>>
+  ) => HKT<F, Separated<HKT<W, RL>, Type2<W, WL, RR>>>
+}
+
+/**
+ * @see Wilt
+ * @since 1.7.0
+ */
+export interface Wilt3<W extends URIS3> {
+  <F extends URIS3>(F: Applicative3<F>): <WU, WL, FU, FL, RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <WU, WL, RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+  <F extends URIS2>(F: Applicative2<F>): <WU, WL, FL, RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <WU, WL, RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+  <F extends URIS>(F: Applicative1<F>): <WU, WL, RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => Type<F, Either<RL, RR>>
+  ) => Type<F, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+  <F>(F: Applicative<F>): <WU, WL, RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => HKT<F, Either<RL, RR>>
+  ) => HKT<F, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+}
+
+/**
+ * @see Wilt
+ * @since 1.7.0
+ */
+export interface Wilt3C<W extends URIS3, WU, WL> {
+  <F extends URIS3>(F: Applicative3<F>): <FU, FL, RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+  <F extends URIS3, FU, FL>(F: Applicative3C<F, FU, FL>): <RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => Type3<F, FU, FL, Either<RL, RR>>
+  ) => Type3<F, FU, FL, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+  <F extends URIS2>(F: Applicative2<F>): <FL, RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+  <F extends URIS2, FL>(F: Applicative2C<F, FL>): <RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => Type2<F, FL, Either<RL, RR>>
+  ) => Type2<F, FL, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+  <F extends URIS>(F: Applicative1<F>): <RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => Type<F, Either<RL, RR>>
+  ) => Type<F, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+  <F>(F: Applicative<F>): <RL, RR, A>(
+    wa: Type3<W, WU, WL, A>,
+    f: (a: A) => HKT<F, Either<RL, RR>>
+  ) => HKT<F, Separated<HKT<W, RL>, Type3<W, WU, WL, RR>>>
+}
+
+/**
+ * @typeclass
+ * `Witherable` represents data structures which can be _partitioned_ with effects in some {@link Applicative} functor.
+ * - {@link wilt}
+ * - {@link wither}
+ *
+ * @since 1.7.0
+ * @see https://github.com/LiamGoodacre/purescript-filterable/blob/master/src/Data/Witherable.purs
+ */
+export interface Witherable<T> extends Traversable<T>, Filterable<T> {
+  /**
+   * Partition a structure with effects
+   */
+  wilt: Wilt<T>
+
+  /**
+   * Filter a structure  with effects
+   */
+  wither: Wither<T>
+}
+
+/**
+ * @since 1.7.0
+ * @see Witherable
+ */
+export interface Witherable1<T extends URIS> extends Traversable1<T>, Filterable1<T> {
+  wilt: Wilt1<T>
+  wither: Wither1<T>
+}
+
+/**
+ * @since 1.7.0
+ * @see Witherable
+ */
+export interface Witherable2<T extends URIS2> extends Traversable2<T>, Filterable2<T> {
+  wilt: Wilt2<T>
+  wither: Wither2<T>
+}
+
+/**
+ * @since 1.7.0
+ * @see Witherable
+ */
+export interface Witherable2C<T extends URIS2, TL> extends Traversable2C<T, TL>, Filterable2C<T, TL> {
+  wilt: Wilt2C<T, TL>
+  wither: Wither2C<T, TL>
+}
+
+/**
+ * @since 1.7.0
+ * @see Witherable
+ */
+export interface Witherable3<T extends URIS3> extends Traversable3<T>, Filterable3<T> {
+  wilt: Wilt3<T>
+  wither: Wither3<T>
+}
+
+/**
+ * @since 1.7.0
+ * @see Witherable
+ */
+export interface Witherable3C<T extends URIS3, TU, TL> extends Traversable3C<T, TU, TL>, Filterable3C<T, TU, TL> {
+  wilt: Wilt3C<T, TU, TL>
+  wither: Wither3C<T, TU, TL>
+}
diff --git a/src/index.ts b/src/index.ts
index 9ab41c21..bc295d7f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -148,3 +148,9 @@ import * as validation from './Validation'
 export { validation }
 import * as writer from './Writer'
 export { writer }
+import * as compactable from './Compactable'
+export { compactable }
+import * as filterable from './Filterable'
+export { filterable }
+import * as witherable from './Witherable'
+export { witherable }
diff --git a/tsconfig.tslint.json b/tsconfig.tslint.json
new file mode 100644
index 00000000..b3750f28
--- /dev/null
+++ b/tsconfig.tslint.json
@@ -0,0 +1,7 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "noEmit": true
+  },
+  "include": ["./test/**/*"]
+}
diff --git a/tslint.json b/tslint.json
index c535d309..f02598c6 100644
--- a/tslint.json
+++ b/tslint.json
@@ -6,6 +6,8 @@
     "variable-name": false,
     "quotemark": [true, "single", "jsx-double"],
     "ter-indent": false,
-    "strict-boolean-expressions": true
+    "strict-boolean-expressions": true,
+    "forin": true,
+    "no-console": true
   }
 }


Test patch:
diff --git a/test/Array.ts b/test/Array.ts
index ab4c13d6..5e8d22b3 100644
--- a/test/Array.ts
+++ b/test/Array.ts
@@ -54,6 +54,9 @@ import { contramap as contramapOrd, ordNumber, ordString } from '../src/Ord'
 import { contramap, getArraySetoid, setoidBoolean, setoidNumber, setoidString } from '../src/Setoid'
 import { traverse } from '../src/Traversable'
 import { identity, tuple } from '../src/function'
+import { Identity, identity as I } from '../src/Identity'
+
+const p = (n: number) => n > 2
 
 describe('Array', () => {
   const as = [1, 2, 3]
@@ -225,18 +228,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 +263,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 +275,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])
   })
@@ -435,4 +417,57 @@ describe('Array', () => {
       { name: 'b', age: 3 }
     ])
   })
+
+  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', () => {
+    assert.deepEqual(array.partition([], p), { left: [], right: [] })
+    assert.deepEqual(array.partition([1, 3], p), { left: [1], right: [3] })
+  })
+
+  it('wither', () => {
+    const witherIdentity = array.wither(I)
+    const f = (n: number) => new Identity(p(n) ? some(n + 1) : none)
+    assert.deepEqual(witherIdentity([], f), new Identity([]))
+    assert.deepEqual(witherIdentity([1, 3], f), new Identity([4]))
+  })
+
+  it('wilt', () => {
+    const wiltIdentity = array.wilt(I)
+    const f = (n: number) => new Identity(p(n) ? right(n + 1) : left(n - 1))
+    assert.deepEqual(wiltIdentity([], f), new Identity({ left: [], right: [] }))
+    assert.deepEqual(wiltIdentity([1, 3], f), new Identity({ left: [0], right: [4] }))
+  })
 })
diff --git a/test/Either.ts b/test/Either.ts
index 9446594e..5608c87e 100644
--- a/test/Either.ts
+++ b/test/Either.ts
@@ -13,12 +13,17 @@ import {
   tryCatch,
   isLeft,
   isRight,
-  fromRefinement
+  fromRefinement,
+  getCompactable,
+  getFilterable,
+  getWitherable
 } from '../src/Either'
 import { none, option, some } from '../src/Option'
 import { setoidNumber, setoidString } from '../src/Setoid'
 import { traverse } from '../src/Traversable'
 import { failure, success } from '../src/Validation'
+import { monoidString } from '../src/Monoid'
+import { Identity, identity as I } from '../src/Identity'
 
 describe('Either', () => {
   it('fold', () => {
@@ -88,7 +93,7 @@ describe('Either', () => {
     assert.deepEqual(e2, left(new SyntaxError('Unexpected end of JSON input')))
 
     const e3 = tryCatch(() => {
-      throw 'a string'
+      throw 'a string' // tslint:disable-line no-string-throw
     })
     assert.deepEqual(e3, left(new Error('a string')))
 
@@ -248,7 +253,7 @@ describe('Either', () => {
 
   it('refineOrElse', () => {
     type Color = 'red' | 'blue'
-    const isColor = (s: string): s is Color => s === 'red' || s == 'blue'
+    const isColor = (s: string): s is Color => s === 'red' || s === 'blue'
     assert.deepEqual(right('red').refineOrElse(isColor, -1), right('red'))
     assert.deepEqual(right('foo').refineOrElse(isColor, -1), left(-1))
     assert.deepEqual(left<number, string>(12).refineOrElse(isColor, -1), left(12))
@@ -256,7 +261,7 @@ describe('Either', () => {
 
   it('refineOrElseL', () => {
     type Color = 'red' | 'blue'
-    const isColor = (s: string): s is Color => s === 'red' || s == 'blue'
+    const isColor = (s: string): s is Color => s === 'red' || s === 'blue'
     const errorHandler = (s: string) => `invalid color ${s}`
     assert.deepEqual(right('red').refineOrElseL(isColor, errorHandler), right('red'))
     assert.deepEqual(right('foo').refineOrElseL(isColor, errorHandler), left('invalid color foo'))
@@ -265,9 +270,94 @@ describe('Either', () => {
 
   it('fromRefinement', () => {
     type Color = 'red' | 'blue'
-    const isColor = (s: string): s is Color => s === 'red' || s == 'blue'
+    const isColor = (s: string): s is Color => s === 'red' || s === 'blue'
     const from = fromRefinement(isColor, s => `invalid color ${s}`)
     assert.deepEqual(from('red'), right('red'))
     assert.deepEqual(from('foo'), left('invalid color foo'))
   })
+
+  describe('getCompactable', () => {
+    const C = getCompactable(monoidString)
+    it('compact', () => {
+      assert.deepEqual(C.compact(left('1')), left('1'))
+      assert.deepEqual(C.compact(right(none)), left(monoidString.empty))
+      assert.deepEqual(C.compact(right(some(123))), right(123))
+    })
+
+    it('separate', () => {
+      assert.deepEqual(C.separate(left('123')), { left: left('123'), right: left('123') })
+      assert.deepEqual(C.separate(right(left('123'))), { left: right('123'), right: left(monoidString.empty) })
+      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))
+    })
+  })
+
+  describe('getWitherable', () => {
+    const W = getWitherable(monoidString)
+    const p = (n: number) => n > 2
+    it('wither', () => {
+      const f = (n: number) => new Identity(p(n) ? some(n + 1) : none)
+      const witherIdentity = W.wither(I)
+      assert.deepEqual(witherIdentity(left<string, number>('foo'), f), new Identity(left('foo')))
+      assert.deepEqual(witherIdentity(right<string, number>(1), f), new Identity(left(monoidString.empty)))
+      assert.deepEqual(witherIdentity(right<string, number>(3), f), new Identity(right(4)))
+    })
+    it('wilt', () => {
+      const wiltIdentity = W.wilt(I)
+      const f = (n: number) => new Identity(p(n) ? right(n + 1) : left(n - 1))
+      assert.deepEqual(
+        wiltIdentity(left<string, number>('foo'), f),
+        new Identity({
+          left: left('foo'),
+          right: left('foo')
+        })
+      )
+      assert.deepEqual(
+        wiltIdentity(right<string, number>(1), f),
+        new Identity({
+          left: right(0),
+          right: left(monoidString.empty)
+        })
+      )
+      assert.deepEqual(
+        wiltIdentity(right<string, number>(3), f),
+        new Identity({
+          left: left(monoidString.empty),
+          right: right(4)
+        })
+      )
+    })
+  })
 })
diff --git a/test/Exception.ts b/test/Exception.ts
index b9cbd7b8..c38300c7 100644
--- a/test/Exception.ts
+++ b/test/Exception.ts
@@ -50,7 +50,7 @@ describe('Exception', () => {
     assert.strictEqual(
       catchError(
         new IO(() => {
-          throw 'bum!'
+          throw 'bum!' // tslint:disable-line no-string-throw
         }),
         e => new IO(() => 1)
       ).run(),
diff --git a/test/Field.ts b/test/Field.ts
index 2e61408a..4a193c67 100644
--- a/test/Field.ts
+++ b/test/Field.ts
@@ -1,20 +1,19 @@
 import * as assert from 'assert'
-import * as field from '../src/Field'
+import { fieldNumber, gcd, lcm } from '../src/Field'
 import * as setoid from '../src/Setoid'
-import { fieldNumber } from '../src/Field'
 
 describe('Field', () => {
   it('gcd', () => {
-    const gcd = field.gcd(setoid.setoidNumber, field.fieldNumber)
-    assert.strictEqual(gcd(10, 5), 5)
-    assert.strictEqual(gcd(10, 2), 2)
-    assert.strictEqual(gcd(10, 3), 1)
+    const gcdNumber = gcd(setoid.setoidNumber, fieldNumber)
+    assert.strictEqual(gcdNumber(10, 5), 5)
+    assert.strictEqual(gcdNumber(10, 2), 2)
+    assert.strictEqual(gcdNumber(10, 3), 1)
   })
 
   it('lcm', () => {
-    const lcm = field.lcm(setoid.setoidNumber, field.fieldNumber)
-    assert.strictEqual(lcm(4, 6), 12)
-    assert.strictEqual(lcm(4, 0), 0)
+    const lcmNumber = lcm(setoid.setoidNumber, fieldNumber)
+    assert.strictEqual(lcmNumber(4, 6), 12)
+    assert.strictEqual(lcmNumber(4, 0), 0)
   })
 
   it('fieldNumber', () => {
diff --git a/test/IO.ts b/test/IO.ts
index d8ae15a1..7db3bceb 100644
--- a/test/IO.ts
+++ b/test/IO.ts
@@ -41,31 +41,31 @@ describe('IO', () => {
     assert.deepEqual(log, ['a', 'b'])
   })
 
-  it('applySecond', () => {
+  it('toString', () => {
+    assert.strictEqual(io.of(1).toString(), 'new IO(<function0>)')
+    assert.strictEqual(io.of(1).inspect(), 'new IO(<function0>)')
+  })
+
+  it('applyFirst', () => {
     const log: Array<string> = []
     const append = (message: string): IO<number> => new IO(() => log.push(message))
     assert.strictEqual(
       append('a')
-        .applySecond(append('b'))
+        .applyFirst(append('b'))
         .run(),
-      2
+      1
     )
     assert.deepEqual(log, ['a', 'b'])
   })
 
-  it('toString', () => {
-    assert.strictEqual(io.of(1).toString(), 'new IO(<function0>)')
-    assert.strictEqual(io.of(1).inspect(), 'new IO(<function0>)')
-  })
-
-  it('applyFirst', () => {
+  it('applySecond', () => {
     const log: Array<string> = []
     const append = (message: string): IO<number> => new IO(() => log.push(message))
     assert.strictEqual(
       append('a')
-        .applyFirst(append('b'))
+        .applySecond(append('b'))
         .run(),
-      1
+      2
     )
     assert.deepEqual(log, ['a', 'b'])
   })
diff --git a/test/IxIO.ts b/test/IxIO.ts
index 8cc7be49..3f503cc3 100644
--- a/test/IxIO.ts
+++ b/test/IxIO.ts
@@ -31,7 +31,7 @@ class Open extends Operation<'Closed', 'Open', number> {
   constructor() {
     super(
       new io.IO(() => {
-        log.push(`Opening the door`)
+        log.push('Opening the door')
         return 1
       })
     )
@@ -41,8 +41,7 @@ class Close extends Operation<'Open', 'Closed', void> {
   constructor() {
     super(
       new io.IO(() => {
-        log.push(`Closing the door`)
-        return undefined
+        log.push('Closing the door')
       })
     )
   }
@@ -51,8 +50,7 @@ class RingBell extends Operation<'Closed', 'Closed', void> {
   constructor() {
     super(
       new io.IO(() => {
-        log.push(`Ringing the bell`)
-        return undefined
+        log.push('Ringing the bell')
       })
     )
   }
diff --git a/test/Option.ts b/test/Option.ts
index 7d73ca94..4be115f2 100644
--- a/test/Option.ts
+++ b/test/Option.ts
@@ -24,6 +24,9 @@ import { semigroupString } from '../src/Semigroup'
 import { setoidNumber } from '../src/Setoid'
 import { traverse } from '../src/Traversable'
 import { identity } from '../src/function'
+import { Identity, identity as I } from '../src/Identity'
+
+const p = (n: number): boolean => n > 2
 
 describe('Option', () => {
   it('fold', () => {
@@ -180,7 +183,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))
   })
@@ -242,15 +245,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'
@@ -285,4 +279,64 @@ describe('Option', () => {
     assert.deepEqual(parseDirection('asc'), some('asc'))
     assert.deepEqual(parseDirection('foo'), none)
   })
+
+  it('compact', () => {
+    assert.deepEqual(option.compact(none), none)
+    assert.deepEqual(option.compact(some(none)), none)
+    assert.deepEqual(option.compact(some(some('123'))), some('123'))
+  })
+
+  it('separate', () => {
+    assert.deepEqual(option.separate(none), { left: none, right: none })
+    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) })
+  })
+
+  it('wither', () => {
+    const witherIdentity = option.wither(I)
+    const f = (n: number) => new Identity(p(n) ? some(n + 1) : none)
+    assert.deepEqual(witherIdentity(none, f), new Identity(none))
+    assert.deepEqual(witherIdentity(some(1), f), new Identity(none))
+    assert.deepEqual(witherIdentity(some(3), f), new Identity(some(4)))
+  })
+
+  it('wilt', () => {
+    const wiltIdentity = option.wilt(I)
+    const f = (n: number) => new Identity(p(n) ? right(n + 1) : left(n - 1))
+    assert.deepEqual(wiltIdentity(none, f), new Identity({ left: none, right: none }))
+    assert.deepEqual(wiltIdentity(some(1), f), new Identity({ left: some(0), right: none }))
+    assert.deepEqual(wiltIdentity(some(3), f), new Identity({ left: none, right: some(4) }))
+  })
 })
diff --git a/test/Random.ts b/test/Random.ts
index fc153f0c..54abbfbc 100644
--- a/test/Random.ts
+++ b/test/Random.ts
@@ -18,7 +18,6 @@ describe('Random', () => {
   it('randomRange', () => {
     for (let i = 0; i < 10; i++) {
       const n = randomRange(0, 10).run()
-      console.log(n)
       assert.strictEqual(typeof n, 'number')
       assert.strictEqual(n >= 0, true)
       assert.strictEqual(n < 10, true)
diff --git a/test/State.ts b/test/State.ts
index dcb82724..cbaf0c4f 100644
--- a/test/State.ts
+++ b/test/State.ts
@@ -50,4 +50,18 @@ describe('State', () => {
     assert.deepEqual(x.chain(f).run(0), [0, 2])
     assert.deepEqual(state.chain(x, f).run(0), [0, 2])
   })
+
+  it('applyFirst', () => {
+    type S = Array<string>
+    const fa = gets<S, number>(() => 1)
+    const fb = gets<S, string>(() => 'foo')
+    assert.strictEqual(fa.applyFirst(fb).eval([]), 1)
+  })
+
+  it('applySecond', () => {
+    type S = Array<string>
+    const fa = gets<S, number>(() => 1)
+    const fb = gets<S, string>(() => 'foo')
+    assert.strictEqual(fa.applySecond(fb).eval([]), 'foo')
+  })
 })
diff --git a/test/StrMap.ts b/test/StrMap.ts
index 3cb7aec9..4eb3f6a6 100644
--- a/test/StrMap.ts
+++ b/test/StrMap.ts
@@ -20,6 +20,10 @@ import {
 } from '../src/StrMap'
 import { traverse } from '../src/Traversable'
 import { semigroupSum } from '../src/Semigroup'
+import { left, right } from '../src/Either'
+import { identity as I, Identity } from '../src/Identity'
+
+const p = (n: number) => n > 2
 
 describe('StrMap', () => {
   it('getMonoid', () => {
@@ -127,8 +131,63 @@ describe('StrMap', () => {
     assert.deepEqual(insert('c', 3, new StrMap({ a: 1, b: 2 })), new StrMap({ a: 1, b: 2, c: 3 }))
   })
 
+  it('compact', () => {
+    assert.deepEqual(strmap.compact(new StrMap({ foo: none, bar: some(123) })), new StrMap({ bar: 123 }))
+  })
+
+  it('separate', () => {
+    assert.deepEqual(strmap.separate(new StrMap({ foo: left(123), bar: right(123) })), {
+      left: new StrMap({ foo: 123 }),
+      right: new StrMap({ bar: 123 })
+    })
+  })
+
   it('filter', () => {
-    const d = new StrMap({ a: 1, b: 2 })
-    assert.deepEqual(d.filter(a => a === 1), new StrMap({ a: 1 }))
+    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 })
+    })
+  })
+
+  it('wither', () => {
+    const witherIdentity = strmap.wither(I)
+    const f = (n: number) => new Identity(p(n) ? some(n + 1) : none)
+    assert.deepEqual(witherIdentity(new StrMap<number>({}), f), new Identity(new StrMap({})))
+    assert.deepEqual(witherIdentity(new StrMap({ a: 1, b: 3 }), f), new Identity(new StrMap({ b: 4 })))
+  })
+
+  it('wilt', () => {
+    const wiltIdentity = strmap.wilt(I)
+    const f = (n: number) => new Identity(p(n) ? right(n + 1) : left(n - 1))
+    assert.deepEqual(
+      wiltIdentity(new StrMap<number>({}), f),
+      new Identity({ left: new StrMap({}), right: new StrMap({}) })
+    )
+    assert.deepEqual(
+      wiltIdentity(new StrMap({ a: 1, b: 3 }), f),
+      new Identity({ left: new StrMap({ a: 0 }), right: new StrMap({ b: 4 }) })
+    )
   })
 })
diff --git a/test/Validation.ts b/test/Validation.ts
index 66c09bf1..b1e70687 100644
--- a/test/Validation.ts
+++ b/test/Validation.ts
@@ -1,6 +1,5 @@
 import * as assert from 'assert'
 import { array } from '../src/Array'
-import * as either from '../src/Either'
 import { monoidString, monoidSum } from '../src/Monoid'
 import { none, option, some } from '../src/Option'
 import { getArraySemigroup, semigroupString } from '../src/Semigroup'
@@ -19,8 +18,15 @@ import {
   isSuccess,
   success,
   validation,
-  getMonoid
+  getMonoid,
+  getCompactable,
+  getFilterable,
+  getWitherable
 } from '../src/Validation'
+import { left, right } from '../src/Either'
+import { identity as I, Identity } from '../src/Identity'
+
+const p = (n: number): boolean => n > 2
 
 describe('Validation', () => {
   it('getMonad', () => {
@@ -53,8 +59,8 @@ describe('Validation', () => {
   })
 
   it('fromEither', () => {
-    assert.deepEqual(fromEither(either.right<string, number>(1)), success(1))
-    assert.deepEqual(fromEither(either.left<string, number>('error')), failure('error'))
+    assert.deepEqual(fromEither(right<string, number>(1)), success(1))
+    assert.deepEqual(fromEither(left<string, number>('error')), failure('error'))
   })
 
   it('getSetoid', () => {
@@ -147,10 +153,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', () => {
@@ -174,4 +179,99 @@ describe('Validation', () => {
     assert.deepEqual(M.concat(failure('foo'), success(1)), failure('foo'))
     assert.deepEqual(M.concat(failure('foo'), failure('bar')), failure('foobar'))
   })
+
+  describe('getCompactable', () => {
+    const C = getCompactable(monoidString)
+    it('compact', () => {
+      assert.deepEqual(C.compact(failure('1')), failure('1'))
+      assert.deepEqual(C.compact(success(none)), failure(monoidString.empty))
+      assert.deepEqual(C.compact(success(some(123))), success(123))
+    })
+
+    it('separate', () => {
+      assert.deepEqual(C.separate(failure('123')), { left: failure('123'), right: failure('123') })
+      assert.deepEqual(C.separate(success(left('123'))), { left: success('123'), right: failure(monoidString.empty) })
+      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))
+    })
+  })
+  describe('getWitherable', () => {
+    const W = getWitherable(monoidString)
+    const p = (n: number) => n > 2
+    it('wither', () => {
+      const f = (n: number) => new Identity(p(n) ? some(n + 1) : none)
+      const witherIdentity = W.wither(I)
+      assert.deepEqual(witherIdentity(failure<string, number>('foo'), f), new Identity(failure('foo')))
+      assert.deepEqual(witherIdentity(success<string, number>(1), f), new Identity(failure(monoidString.empty)))
+      assert.deepEqual(witherIdentity(success<string, number>(3), f), new Identity(success(4)))
+    })
+    it('wilt', () => {
+      const wiltIdentity = W.wilt(I)
+      const f = (n: number) => new Identity(p(n) ? right(n + 1) : left(n - 1))
+      assert.deepEqual(
+        wiltIdentity(failure<string, number>('foo'), f),
+        new Identity({
+          left: failure('foo'),
+          right: failure('foo')
+        })
+      )
+      assert.deepEqual(
+        wiltIdentity(success<string, number>(1), f),
+        new Identity({
+          left: success(0),
+          right: failure(monoidString.empty)
+        })
+      )
+      assert.deepEqual(
+        wiltIdentity(success<string, number>(3), f),
+        new Identity({
+          left: failure(monoidString.empty),
+          right: success(4)
+        })
+      )
+    })
+  })
 })