| 1 | /** |
| 2 | * @since 2.0.0 |
| 3 | */ |
| 4 | import * as Equal from "../Equal.ts" |
| 5 | import { format } from "../Formatter.ts" |
| 6 | import * as Hash from "../Hash.ts" |
| 7 | import { toJson } from "../Inspectable.ts" |
| 8 | import type * as Option from "../Option.ts" |
| 9 | import { hasProperty } from "../Predicate.ts" |
| 10 | import { SingleShotGen } from "../Utils.ts" |
| 11 | import { PipeInspectableProto } from "./core.ts" |
| 12 | |
| 13 | const TypeId = "~effect/Option" |
| 14 | |
| 15 | const CommonProto = { |
| 16 | [TypeId]: { |
| 17 | _A: (_: never) => _ |
| 18 | }, |
| 19 | ...PipeInspectableProto, |
| 20 | [Symbol.iterator]() { |
| 21 | return new SingleShotGen(this) |
| 22 | } |
| 23 | } |
| 24 | |
| 25 | // `valueOrUndefined` is folded into the initializer (rather than a separate |
| 26 | // `Object.defineProperty(SomeProto, ...)` statement) so the whole definition |
| 27 | // is pure-annotated by the build and tree-shakable. |
| 28 | const SomeProto = Object.defineProperty( |
| 29 | Object.assign(Object.create(CommonProto), { |
| 30 | _tag: "Some", |
| 31 | _op: "Some", |
| 32 | [Equal.symbol]<A>(this: Option.Some<A>, that: unknown): boolean { |
| 33 | return ( |
| 34 | isOption(that) && isSome(that) && Equal.equals(this.value, that.value) |
| 35 | ) |
| 36 | }, |
| 37 | [Hash.symbol]<A>(this: Option.Some<A>) { |
| 38 | return Hash.combine(Hash.hash(this._tag))(Hash.hash(this.value)) |
| 39 | }, |
| 40 | toString<A>(this: Option.Some<A>) { |
| 41 | return `some(${format(this.value)})` |
| 42 | }, |
| 43 | toJSON<A>(this: Option.Some<A>) { |
| 44 | return { |
| 45 | _id: "Option", |
| 46 | _tag: this._tag, |
| 47 | value: toJson(this.value) |
| 48 | } |
| 49 | } |
| 50 | }), |
| 51 | "valueOrUndefined", |
| 52 | { |
| 53 | get() { |
| 54 | return this.value |
| 55 | } |
| 56 | } |
| 57 | ) |
| 58 | |
| 59 | const NoneHash = Hash.hash("None") |
| 60 | const NoneProto = Object.assign(Object.create(CommonProto), { |
| 61 | _tag: "None", |
| 62 | _op: "None", |
| 63 | valueOrUndefined: undefined, |
| 64 | [Equal.symbol]<A>(this: Option.None<A>, that: unknown): boolean { |
| 65 | return isOption(that) && isNone(that) |
| 66 | }, |
| 67 | [Hash.symbol]<A>(this: Option.None<A>) { |
| 68 | return NoneHash |
| 69 | }, |
| 70 | toString<A>(this: Option.None<A>) { |
| 71 | return `none()` |
| 72 | }, |
| 73 | toJSON<A>(this: Option.None<A>) { |
| 74 | return { |
| 75 | _id: "Option", |
| 76 | _tag: this._tag |
| 77 | } |
| 78 | } |
| 79 | }) |
| 80 | |
| 81 | /** @internal */ |
| 82 | export const isOption = (input: unknown): input is Option.Option<unknown> => hasProperty(input, TypeId) |
| 83 | |
| 84 | /** @internal */ |
| 85 | export const isNone = <A>(fa: Option.Option<A>): fa is Option.None<A> => fa._tag === "None" |
| 86 | |
| 87 | /** @internal */ |
| 88 | export const isSome = <A>(fa: Option.Option<A>): fa is Option.Some<A> => fa._tag === "Some" |
| 89 | |
| 90 | /** @internal */ |
| 91 | export const none: Option.Option<never> = Object.create(NoneProto) |
| 92 | |
| 93 | /** @internal */ |
| 94 | const SomeImpl = function(this: any, value: unknown) { |
| 95 | this.value = value |
| 96 | } as unknown as { new<A>(value: A): Option.Option<A>; prototype: any } |
| 97 | SomeImpl.prototype = SomeProto |
| 98 | |
| 99 | /** @internal */ |
| 100 | export const some = <A>(value: A): Option.Option<A> => new SomeImpl(value) |