From 906096a5cb5d60eb98e477fa8a56fc54030bb417 Mon Sep 17 00:00:00 2001
From: Matei Adriel <rafaeladriel11@gmail.com>
Date: Sun, 22 Dec 2019 15:38:16 +0200
Subject: [PATCH] typescript(option): feat: Refactored everything to use
 nominal types typescript(option): typescript(option): BREAKING CHANGE: flat
 only works on one layer but is now strongly typed | everything uses nominal
 types now | match has the correct parameter order

Signed-off-by: prescientmoon <git@moonythm.dev>
---
 typescript/option/src/helpers.ts          | 76 ++++++++++-------------
 typescript/option/src/internalHelperts.ts |  1 -
 typescript/option/src/internals.ts        | 28 ++-------
 typescript/option/src/types.ts            | 18 ++++--
 4 files changed, 51 insertions(+), 72 deletions(-)

diff --git a/typescript/option/src/helpers.ts b/typescript/option/src/helpers.ts
index 296edd2..5f28819 100644
--- a/typescript/option/src/helpers.ts
+++ b/typescript/option/src/helpers.ts
@@ -7,48 +7,48 @@ import {
     BackFolder,
     Nullable
 } from './internalTypes'
-import { always, identity } from './internalHelperts'
-import Internals, { SomeClass, isOption } from './internals'
+import { identity } from './internalHelperts'
+import { none, some } from './internals'
 
-export const isSome = <T>(option: Option<T>) =>
-    option instanceof Internals.SomeClass
-export const isNothing = <T>(option: Option<T>) =>
-    option instanceof Internals.NoneClass
+export const isSome = <T>(option: Option<T>): option is Some<T> =>
+    option.type === some
+export const isNothing = <T>(option: Option<T>): option is None =>
+    option.type === none
 
-export const match = <T, U>(
-    option: Option<T>,
+const match = <T, U>(
     caseSome: Mapper<T, U>,
-    caseNone: Mapper<void, U>
+    _default: U,
+    option: Option<T>
 ) => {
     if (isSome(option)) {
-        return caseSome((option as SomeClass<T>)[Internals.someValue])
+        return caseSome(option.value as T)
     }
 
-    return caseNone()
+    return _default
 }
 
 export const bind = <T, U>(
     binder: Binder<T, U>,
     option: Option<T>
 ): Option<U> => {
-    return match(option, binder, always(None))
+    return match(binder, None, option)
 }
 
 export const map = <T, U>(
     mapper: Mapper<T, U>,
     option: Option<T>
 ): Option<U> => {
-    return match(option, v => Some(mapper(v)), always(None))
+    return match(v => Some(mapper(v)), None, option)
 }
 
 export const count = <T>(option: Option<T>) => Number(isSome(option))
 
 export const exists = <T>(predicate: Predicate<T>, option: Option<T>) => {
-    return match(option, predicate, always(false))
+    return match(predicate, false, option)
 }
 
 export const filter = <T>(predicate: Predicate<T>, option: Option<T>) => {
-    return match(option, v => (predicate(v) ? Some(v) : None), always(None))
+    return match(v => (predicate(v) ? Some(v) : None), None, option)
 }
 
 export const fold = <T, U>(
@@ -56,7 +56,7 @@ export const fold = <T, U>(
     initial: U,
     option: Option<T>
 ) => {
-    return match(option, v => folder(initial, v), always(initial))
+    return match(v => folder(initial, v), initial, option)
 }
 
 export const foldback = <T, U>(
@@ -64,53 +64,41 @@ export const foldback = <T, U>(
     option: Option<T>,
     initial: U
 ) => {
-    return match(option, v => folder(v, initial), always(initial))
+    return match(v => folder(v, initial), initial, option)
 }
 
 export const forall = <T>(predicate: Predicate<T>, option: Option<T>) => {
-    return match(option, predicate, () => true)
+    return match(predicate, true, option)
 }
 
-export const get = <T>(option: Option<T>) => {
-    return match(
-        option,
-        v => v,
-        () => {
-            throw new Error('Cannot get value from None')
-        }
-    )
+export const get = <T>(option: Option<T>): T => {
+    if (isSome(option)) {
+        return option.value
+    }
+
+    throw new Error(`Cannot get value of None`)
 }
 
 export const iter = <T>(mapper: Mapper<T, void>, option: Option<T>) => {
-    return match(option, mapper, always(None))
+    if (isSome(option)) {
+        mapper(option.value)
+    }
 }
 
 export const toArray = <T>(option: Option<T>) => {
-    return match(option, v => [v], always([]))
+    return match(v => [v], [], option)
 }
 
 export const toNullable = <T>(option: Option<T>) => {
-    return match(option, identity, always(null))
+    return match(identity, null, option)
 }
 
 export const withDefault = <T>(_default: T, option: Option<T>) => {
-    return match(option, identity, always(_default))
+    return match(identity, _default, option)
 }
 
-const checkIfOption = <T>(x): x is Option<T> => x[isOption]
-
-export const flat = <T, U>(option: Option<T>): Option<U> => {
-    return match(
-        option,
-        inner => {
-            if (checkIfOption(inner)) {
-                return flat(inner)
-            } else {
-                return Some(inner)
-            }
-        },
-        always(None)
-    )
+export const flat = <T>(option: Option<Option<T>>): Option<T> => {
+    return bind(identity, option)
 }
 
 export const fromNullable = <T>(value: Nullable<T>): Option<T> => {
diff --git a/typescript/option/src/internalHelperts.ts b/typescript/option/src/internalHelperts.ts
index 693472b..d1600f3 100644
--- a/typescript/option/src/internalHelperts.ts
+++ b/typescript/option/src/internalHelperts.ts
@@ -1,2 +1 @@
-export const always = <T>(v: T) => () => v
 export const identity = <T>(v: T) => v
diff --git a/typescript/option/src/internals.ts b/typescript/option/src/internals.ts
index 2ec597a..f4845fd 100644
--- a/typescript/option/src/internals.ts
+++ b/typescript/option/src/internals.ts
@@ -1,25 +1,7 @@
-export const isOption = Symbol('option')
-export const someValue = Symbol('value')
+export const some = Symbol('some')
+export const none = Symbol('none')
 
-export class SomeClass<T> {
-    public [isOption] = true
-    public [someValue]: T
-
-    public constructor(value: T) {
-        this[someValue] = value
-    }
-
-    public toString() {
-        return `Some(${this[someValue]})`
-    }
+export type NominalTyped<T, U> = {
+    type: T
+    value: U
 }
-
-export class NoneClass {
-    public [isOption] = true
-
-    public toString() {
-        return 'None'
-    }
-}
-
-export default { NoneClass, SomeClass, isOption, someValue }
diff --git a/typescript/option/src/types.ts b/typescript/option/src/types.ts
index 3f55148..ff1d556 100644
--- a/typescript/option/src/types.ts
+++ b/typescript/option/src/types.ts
@@ -1,6 +1,16 @@
-import * as Internals from './internals'
+import { NominalTyped, none, some } from './internals'
 
-export type Option<T> = Internals.SomeClass<T> | Internals.NoneClass
+export type None = NominalTyped<typeof none, null>
+export type Some<T> = NominalTyped<typeof some, T>
 
-export const None = new Internals.NoneClass()
-export const Some = <T>(v: T) => new Internals.SomeClass(v)
+export type Option<T> = Some<T> | None
+
+export const None: Option<any> = {
+    type: none,
+    value: null
+}
+
+export const Some = <T>(value: T): Option<T> => ({
+    type: some,
+    value
+})