diff --git a/CHANGELOG.md b/CHANGELOG.md
index 541bdb1..d05cfba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,13 @@
**Note**: Gaps between patch versions are faulty/broken releases. **Note**: A feature tagged as Experimental is in a
high state of flux, you're at risk of it changing without notice.
+# 2.4.0
+
+- **New Feature**
+ - `Tuple`
+ - add `_1` constructor (@toastal)
+ - add `_2` constructor (@toastal)
+
# 2.3.11
- **Bug Fix**
diff --git a/package.json b/package.json
index c50a350..f90a164 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "monocle-ts",
- "version": "2.3.11",
+ "version": "2.4.0",
"description": "A porting of scala monocle library to TypeScript",
"main": "lib/index.js",
"module": "es6/index.js",
diff --git a/src/Tuple.ts b/src/Tuple.ts
new file mode 100644
index 0000000..70ae66d
--- /dev/null
+++ b/src/Tuple.ts
@@ -0,0 +1,24 @@
+/**
+ * @since 2.4.0
+ */
+import { fst, snd, map, mapLeft } from 'fp-ts/lib/Tuple'
+import { constant } from 'fp-ts/lib/function'
+import { Lens } from '.'
+
+// TODO on fp-ts upgrade: map → mapFst
+const one = new Lens<[any, any], any>(fst, (x) => map(constant(x)))
+
+/**
+ * @category constructor
+ * @since 2.4.0
+ */
+export const _1 = (): Lens<[A, B], A> => one
+
+// TODO on fp-ts upgrade: mapLeft → mapSnd
+const two = new Lens<[any, any], any>(snd, (y) => mapLeft(constant(y)))
+
+/**
+ * @category constructor
+ * @since 2.4.0
+ */
+export const _2 = (): Lens<[A, B], B> => two
diff --git a/test/Tuple.ts b/test/Tuple.ts
new file mode 100644
index 0000000..8bbec4d
--- /dev/null
+++ b/test/Tuple.ts
@@ -0,0 +1,17 @@
+import * as assert from 'assert'
+import { _1, _2 } from '../src/Tuple'
+import * as U from './util'
+
+describe('Tuple', () => {
+ const mock: [number, string] = [1, 'a']
+
+ it('gets', () => {
+ assert.strictEqual(_1().get(mock), 1)
+ assert.strictEqual(_2().get(mock), 'a')
+ })
+
+ it('sets', () => {
+ U.deepStrictEqual(_1().set(2)(mock), [2, 'a'])
+ U.deepStrictEqual(_2().set('b')(mock), [1, 'b'])
+ })
+})