Repository: gcanti/fp-ts
Pull request #501 (2018-06-27 06:28:00)

Bug Fix: fix Set.partitionMap signature


Patch:
diff --git a/docs-generator/domain.ts b/docs-generator/domain.ts
index 5366ea8b..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
   ) {}
 }
 
@@ -40,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/markdown.ts b/docs-generator/markdown.ts
index 4da78d79..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)
@@ -127,7 +130,7 @@ const printData = (d: Data): string => {
 }
 
 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)
@@ -136,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)
@@ -145,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) {
@@ -158,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 6b0de992..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 => {
@@ -278,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))
           }
         }
       }
@@ -315,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/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/package.json b/package.json
index 8ce38e4b..bef60333 100644
--- a/package.json
+++ b/package.json
@@ -8,12 +8,12 @@
   "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 --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",
+    "typings": "typings-checker --allow-expect-error --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/*",
     "build": "npm run clean && tsc",
diff --git a/src/Apply.ts b/src/Apply.ts
index 613caeac..8f1f9564 100644
--- a/src/Apply.ts
+++ b/src/Apply.ts
@@ -340,11 +340,11 @@ export function sequenceT<F extends URIS2, L>(F: Apply2C<F, L>): SequenceT2C<F,
 export function sequenceT<F extends URIS>(F: Apply1<F>): SequenceT1<F>
 export function sequenceT<F>(F: Apply<F>): SequenceT<F>
 export function sequenceT<F>(F: Apply<F>): SequenceT<F> {
-  return (...args: any[]) => {
+  return (...args: Array<any>) => {
     const len = args.length
     let f = tupleConstructors[len]
     if (!Boolean(f)) {
-      f = tupleConstructors[len] = curried((...args: any[]): any[] => args, len - 1, [])
+      f = tupleConstructors[len] = curried((...args: Array<any>): Array<any> => args, len - 1, [])
     }
     let r = F.map(args[0], f)
     for (let i = 1; i < len; i++) {
diff --git a/src/Array.ts b/src/Array.ts
index 0fea391a..7eaa64da 100644
--- a/src/Array.ts
+++ b/src/Array.ts
@@ -17,6 +17,7 @@ import { Unfoldable1 } from './Unfoldable'
 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
 
@@ -56,7 +57,7 @@ export const getMonoid = <A = never>(): Monoid<Array<A>> => {
  * @constant
  * @since 1.0.0
  */
-export const getSetoid: <A>(S: Setoid<A>) => Setoid<A[]> = getArraySetoid
+export const getSetoid: <A>(S: Setoid<A>) => Setoid<Array<A>> = getArraySetoid
 
 /**
  * Derives an Order over the Array of a given element type from the Order, 'O', of that type. The ordering between two
@@ -741,8 +742,8 @@ 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[] = []
+const filterMap = <A, B>(fa: Array<A>, f: (a: A) => Option<B>): Array<B> => {
+  const result: Array<B> = []
   for (const a of fa) {
     const optionB = f(a)
     if (optionB.isSome()) {
@@ -755,9 +756,9 @@ const filterMap = <A, B>(fa: A[], f: (a: A) => Option<B>): B[] => {
  * @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[] = []
+export const partitionMap = <A, L, R>(fa: Array<A>, f: (a: A) => Either<L, R>): Separated<Array<L>, Array<R>> => {
+  const left: Array<L> = []
+  const right: Array<R> = []
   for (const a of fa) {
     const e = f(a)
     if (e.isLeft()) {
@@ -776,10 +777,10 @@ export const partitionMap = <A, L, R>(fa: A[], f: (a: A) => Either<L, R>): Separ
  * @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[] = []
+export const filter = <A>(fa: Array<A>, p: Predicate<A>): Array<A> => fa.filter(p)
+const partition = <A>(fa: Array<A>, p: Predicate<A>): Separated<Array<A>, Array<A>> => {
+  const left: Array<A> = []
+  const right: Array<A> = []
   for (const a of fa) {
     if (p(a)) {
       right.push(a)
@@ -792,8 +793,8 @@ const partition = <A>(fa: A[], p: Predicate<A>): Separated<A[], A[]> => {
     right
   }
 }
-const compact = <A>(fa: Option<A>[]): A[] => {
-  const result: A[] = []
+const compact = <A>(fa: Array<Option<A>>): Array<A> => {
+  const result: Array<A> = []
   for (const optionA of fa) {
     if (optionA.isSome()) {
       result.push(optionA.value)
@@ -801,9 +802,9 @@ const compact = <A>(fa: Option<A>[]): A[] => {
   }
   return result
 }
-const separate = <RL, RR>(fa: Either<RL, RR>[]): Separated<RL[], RR[]> => {
-  const left: RL[] = []
-  const right: RR[] = []
+const separate = <RL, RR>(fa: Array<Either<RL, RR>>): Separated<Array<RL>, Array<RR>> => {
+  const left: Array<RL> = []
+  const right: Array<RR> = []
   for (const e of fa) {
     if (e.isLeft()) {
       left.push(e.value)
@@ -833,6 +834,18 @@ export const catOptions = compact
  */
 export const mapOption = filterMap
 
+const wither = <F>(F: Applicative<F>): (<A, B>(ta: Array<A>, f: (a: A) => HKT<F, Option<B>>) => HKT<F, Array<B>>) => {
+  const traverseF = traverse(F)
+  return (wa, f) => F.map(traverseF(wa, f), compact)
+}
+
+const wilt = <F>(
+  F: Applicative<F>
+): (<RL, RR, A>(wa: Array<A>, f: (a: A) => HKT<F, Either<RL, RR>>) => HKT<F, Separated<Array<RL>, Array<RR>>>) => {
+  const traverseF = traverse(F)
+  return (wa, f) => F.map(traverseF(wa, f), separate)
+}
+
 export const array: Monad1<URI> &
   Foldable1<URI> &
   Unfoldable1<URI> &
@@ -841,7 +854,8 @@ export const array: Monad1<URI> &
   Plus1<URI> &
   Extend1<URI> &
   Compactable1<URI> &
-  Filterable1<URI> = {
+  Filterable1<URI> &
+  Witherable1<URI> = {
   URI,
   map,
   compact,
@@ -858,5 +872,7 @@ export const array: Monad1<URI> &
   traverse,
   zero,
   alt,
-  extend
+  extend,
+  wither,
+  wilt
 }
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/Either.ts b/src/Either.ts
index b40eb16b..ca58eddb 100644
--- a/src/Either.ts
+++ b/src/Either.ts
@@ -14,6 +14,7 @@ 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> {
@@ -474,7 +475,7 @@ export function getCompactable<L>(ML: Monoid<L>): Compactable2C<URI, L> {
 }
 
 /**
- * Builds {@link Filterable} instance for {@link Either} gived {@link Monoid} for the left side
+ * Builds {@link Filterable} instance for {@link Either} given {@link Monoid} for the left side
  * @function
  * @since 1.7.0
  */
@@ -541,6 +542,40 @@ export function getFilterable<L>(ML: Monoid<L>): Filterable2C<URI, L> {
   }
 }
 
+/**
+ * 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/Monoid.ts b/src/Monoid.ts
index 711d31ea..1021c29c 100644
--- a/src/Monoid.ts
+++ b/src/Monoid.ts
@@ -71,7 +71,7 @@ export const monoidAny: Monoid<boolean> = {
   empty: false
 }
 
-const emptyArray: any[] = []
+const emptyArray: Array<any> = []
 
 /**
  * @instance
@@ -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 56b2dd6d..36cd4333 100644
--- a/src/Option.ts
+++ b/src/Option.ts
@@ -14,6 +14,7 @@ import { Traversable1 } from './Traversable'
 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> {
@@ -556,6 +557,26 @@ 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
 }
+
+/**
+ * Returns a refinement from a prism.
+ * This function ensures that a custom type guard definition is type-safe.
+ *
+ * @example
+ * type A = { type: 'A' }
+ * type B = { type: 'B' }
+ * type C = A | B
+ *
+ * const isA = (c: C): c is A => c.type === 'B' // <= typo but typescript doesn't complain
+ * const isA = getRefinement<C, A>(c => (c.type === 'B' ? some(c) : none)) // static error: Type '"B"' is not assignable to type '"A"'
+ *
+ * @function
+ * @since 1.7.0
+ */
+export const getRefinement = <A, B extends A>(getOption: (a: A) => Option<B>): Refinement<A, B> => {
+  return (a: A): a is B => getOption(a).isSome()
+}
+
 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()) {
@@ -586,6 +607,33 @@ const partition = <A>(fa: Option<A>, p: Predicate<A>): Separated<Option<A>, Opti
   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
  * @since 1.0.0
@@ -597,7 +645,8 @@ export const option: Monad1<URI> &
   Alternative1<URI> &
   Extend1<URI> &
   Compactable1<URI> &
-  Filterable1<URI> = {
+  Filterable1<URI> &
+  Witherable1<URI> = {
   URI,
   map,
   of,
@@ -613,5 +662,7 @@ export const option: Monad1<URI> &
   filter,
   filterMap,
   partition,
-  partitionMap
+  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 6df767a1..426b2b99 100644
--- a/src/Set.ts
+++ b/src/Set.ts
@@ -183,21 +183,30 @@ 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>): Separated<Set<L>, Set<R>> => {
+export const partitionMap = <L, R>(SL: Setoid<L>, SR: Setoid<R>) => <A>(
+  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()
-  let r = new Set()
+  let left = new Set()
+  let right = new Set()
+  const isMemberL = member(SL)(left)
+  const isMemberR = member(SR)(right)
   // tslint:disable:no-conditional-assignment
   while (!(e = values.next()).done) {
     const v = f(e.value)
     if (v.isLeft()) {
-      l.add(v.value)
+      if (!isMemberL(v.value)) {
+        left.add(v.value)
+      }
     } else {
-      r.add(v.value)
+      if (!isMemberR(v.value)) {
+        right.add(v.value)
+      }
     }
   }
-  return { left: l, right: r }
+  return { left, right }
 }
 
 /**
@@ -281,7 +290,7 @@ export const remove = <A>(S: Setoid<A>) => (a: A, x: Set<A>): Set<A> => {
  * @function
  * @since 1.2.0
  */
-export const fromArray = <A>(S: Setoid<A>) => (as: A[]): Set<A> => {
+export const fromArray = <A>(S: Setoid<A>) => (as: Array<A>): Set<A> => {
   const len = as.length
   const r = new Set<A>()
   const isMember = member(S)(r)
diff --git a/src/StrMap.ts b/src/StrMap.ts
index ad5e7d1d..cc32b948 100644
--- a/src/StrMap.ts
+++ b/src/StrMap.ts
@@ -13,6 +13,7 @@ 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
 
@@ -36,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)
   }
@@ -119,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
   }
@@ -159,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
 }
 
 /**
@@ -231,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
 }
@@ -384,11 +387,27 @@ const separate = <RL, RR>(fa: StrMap<Either<RL, RR>>): Separated<StrMap<RL>, Str
   }
 }
 
+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> & Compactable1<URI> & Filterable1<URI> = {
+export const strmap: Functor1<URI> &
+  Foldable1<URI> &
+  Traversable1<URI> &
+  Compactable1<URI> &
+  Filterable1<URI> &
+  Witherable1<URI> = {
   URI,
   map,
   reduce,
@@ -398,5 +417,7 @@ export const strmap: Functor1<URI> & Foldable1<URI> & Traversable1<URI> & Compac
   filter,
   filterMap,
   partition,
-  partitionMap
+  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/Tree.ts b/src/Tree.ts
index 1e424926..bd950d93 100644
--- a/src/Tree.ts
+++ b/src/Tree.ts
@@ -128,7 +128,7 @@ const reduce = <A, B>(fa: Tree<A>, b: B, f: (b: B, a: A) => B): B => {
 
 const traverse = <F>(F: Applicative<F>): (<A, B>(ta: Tree<A>, f: (a: A) => HKT<F, B>) => HKT<F, Tree<B>>) => {
   const traverseAF = traverseA(F)
-  const treeLifted: <A>(a: HKT<F, A>) => (b: HKT<F, Tree<A>[]>) => HKT<F, Tree<A>> = liftA2(F)(
+  const treeLifted: <A>(a: HKT<F, A>) => (b: HKT<F, Array<Tree<A>>>) => HKT<F, Tree<A>> = liftA2(F)(
     <A>(value: A) => (forest: Forest<A>): Tree<A> => new Tree(value, forest)
   )
   const r = <A, B>(ta: Tree<A>, f: (a: A) => HKT<F, B>): HKT<F, Tree<B>> =>
@@ -141,7 +141,7 @@ const traverse = <F>(F: Applicative<F>): (<A, B>(ta: Tree<A>, f: (a: A) => HKT<F
  * @since 1.6.0
  */
 export const getSetoid = <A>(S: Setoid<A>): Setoid<Tree<A>> => {
-  let SA: Setoid<Tree<A>[]>
+  let SA: Setoid<Array<Tree<A>>>
   const R: Setoid<Tree<A>> = {
     equals: (x, y) => S.equals(x.value, y.value) && SA.equals(x.forest, y.forest)
   }
@@ -307,7 +307,7 @@ export function unfoldForestM<M>(
   M: Monad<M>
 ): <A, B>(bs: Array<B>, f: (b: B) => HKT<M, [A, Array<B>]>) => HKT<M, Forest<A>> {
   const traverseAM = traverseA(M)
-  let unfoldTreeMM: <A, B>(b: B, f: (b: B) => HKT<M, [A, B[]]>) => HKT<M, Tree<A>>
+  let unfoldTreeMM: <A, B>(b: B, f: (b: B) => HKT<M, [A, Array<B>]>) => HKT<M, Tree<A>>
   return (bs, f) => {
     // tslint:disable-next-line
     if (unfoldTreeMM === undefined) {
diff --git a/src/Validation.ts b/src/Validation.ts
index efe1fffe..61dacad3 100644
--- a/src/Validation.ts
+++ b/src/Validation.ts
@@ -14,6 +14,7 @@ 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
 
@@ -483,6 +484,40 @@ export function getFilterable<L>(ML: Monoid<L>): Filterable2C<URI, L> {
   }
 }
 
+/**
+ * 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 934fdfa0..bc295d7f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -152,3 +152,5 @@ 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..6535265f 100644
--- a/tslint.json
+++ b/tslint.json
@@ -6,6 +6,9 @@
     "variable-name": false,
     "quotemark": [true, "single", "jsx-double"],
     "ter-indent": false,
-    "strict-boolean-expressions": true
+    "strict-boolean-expressions": true,
+    "forin": true,
+    "no-console": true,
+    "array-type": [true, "generic"]
   }
 }
diff --git a/typings-checker/index.ts b/typings-checker/index.ts
index effb3ede..ef4df933 100644
--- a/typings-checker/index.ts
+++ b/typings-checker/index.ts
@@ -6,7 +6,7 @@ import { Const, const_ } from '../src/Const'
 import { Either, either } from '../src/Either'
 import { Functor2C, Functor3C, lift } from '../src/Functor'
 import { getMonad as getIxIOMonad } from '../src/IxIO'
-import { Option, option } from '../src/Option'
+import { Option, option, getRefinement, some, none } from '../src/Option'
 import * as optionT from '../src/OptionT'
 import { Reader, reader } from '../src/Reader'
 import { getArraySemigroup, semigroupString } from '../src/Semigroup'
@@ -99,3 +99,11 @@ declare function apiForTaskify(path: string, callback: (err: Error | null | unde
 const apiTaskified = taskify(apiForTaskify)
 
 type S1 = AssertEquals<typeof apiTaskified, (a: string) => TaskEither<Error, string>, 'T'>
+
+// getRefinement
+
+type A = { type: 'A' }
+type B = { type: 'B' }
+type C = A | B
+// $ExpectError Type '"B"' is not assignable to type '"A"'
+const isA = getRefinement<C, A>(c => c.type === 'B' ? some(c) : none)


Test patch:
diff --git a/test/Array.ts b/test/Array.ts
index 17ee9a9f..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]
@@ -450,8 +453,21 @@ describe('Array', () => {
   })
 
   it('partition', () => {
-    const p = (n: number) => n > 2
     assert.deepEqual(array.partition([], p), { left: [], right: [] })
     assert.deepEqual(array.partition([1, 3], p), { left: [1], right: [3] })
   })
+
+  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 fafe9b6f..5608c87e 100644
--- a/test/Either.ts
+++ b/test/Either.ts
@@ -15,13 +15,15 @@ import {
   isRight,
   fromRefinement,
   getCompactable,
-  getFilterable
+  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', () => {
@@ -321,4 +323,41 @@ describe('Either', () => {
       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/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 a2e4bd23..6c2b34fe 100644
--- a/test/Option.ts
+++ b/test/Option.ts
@@ -17,13 +17,15 @@ import {
   none,
   option,
   some,
-  tryCatch
+  tryCatch,
+  getRefinement
 } from '../src/Option'
 import { ordString } from '../src/Ord'
 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
 
@@ -322,4 +324,33 @@ describe('Option', () => {
     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) }))
+  })
+
+  it('getRefinement', () => {
+    const f = (s: string | number): Option<string> => (typeof s === 'string' ? some(s) : none)
+    const isString = getRefinement(f)
+    assert.strictEqual(isString('s'), true)
+    assert.strictEqual(isString(1), false)
+    type A = { type: 'A' }
+    type B = { type: 'B' }
+    type C = A | B
+    const isA = getRefinement<C, A>(c => (c.type === 'A' ? some(c) : none))
+    assert.strictEqual(isA({ type: 'A' }), true)
+    assert.strictEqual(isA({ type: 'B' }), false)
+  })
 })
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/Set.ts b/test/Set.ts
index cdea7b9b..da8751ad 100644
--- a/test/Set.ts
+++ b/test/Set.ts
@@ -24,7 +24,7 @@ import {
   toArray,
   union
 } from '../src/Set'
-import { Setoid, setoidNumber, setoidString } from '../src/Setoid'
+import { Setoid, setoidNumber, setoidString, getRecordSetoid } from '../src/Setoid'
 
 const gte2 = (n: number) => n >= 2
 
@@ -107,14 +107,29 @@ describe('Set', () => {
   })
 
   it('partitionMap', () => {
-    assert.deepEqual(partitionMap(new Set([]), left), {
+    assert.deepEqual(partitionMap(setoidNumber, setoidString)(new Set([]), left), {
       left: new Set([]),
       right: new Set([])
     })
-    assert.deepEqual(partitionMap(new Set([1, 2, 3]), x => (x % 2 === 0 ? left(x) : right(`${x}`))), {
-      left: new Set([2]),
-      right: new Set(['1', '3'])
-    })
+    assert.deepEqual(
+      partitionMap(setoidNumber, setoidString)(new Set([1, 2, 3]), x => (x % 2 === 0 ? left(x) : right(`${x}`))),
+      {
+        left: new Set([2]),
+        right: new Set(['1', '3'])
+      }
+    )
+    const SL = getRecordSetoid({ value: setoidNumber })
+    const SR = getRecordSetoid({ value: setoidString })
+    assert.deepEqual(
+      partitionMap(SL, SR)(
+        new Set([{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]),
+        x => (x.value % 2 === 0 ? left({ value: 2 }) : right({ value: 'odd' }))
+      ),
+      {
+        left: new Set([{ value: 2 }]),
+        right: new Set([{ value: 'odd' }])
+      }
+    )
   })
 
   it('getUnionMonoid', () => {
diff --git a/test/StrMap.ts b/test/StrMap.ts
index 0c862968..4eb3f6a6 100644
--- a/test/StrMap.ts
+++ b/test/StrMap.ts
@@ -21,6 +21,7 @@ import {
 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
 
@@ -169,4 +170,24 @@ describe('StrMap', () => {
       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 05090e68..b1e70687 100644
--- a/test/Validation.ts
+++ b/test/Validation.ts
@@ -20,9 +20,11 @@ import {
   validation,
   getMonoid,
   getCompactable,
-  getFilterable
+  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
 
@@ -236,4 +238,40 @@ describe('Validation', () => {
       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)
+        })
+      )
+    })
+  })
 })