Skip to content

Commit

Permalink
refactor: use more Maybe
Browse files Browse the repository at this point in the history
  • Loading branch information
exuanbo committed Dec 6, 2023
1 parent 5e567cb commit bf43881
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 259 deletions.
168 changes: 0 additions & 168 deletions __tests__/common/maybe.test.ts

This file was deleted.

89 changes: 53 additions & 36 deletions src/common/maybe.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,74 @@
import type { Nullable } from './utils/types'
// Maybe monad implementation, API inspired by https://github.com/gigobyte/purify
// Reference documentation https://gigobyte.github.io/purify/adts/Maybe
// ISC licensed https://github.com/gigobyte/purify/blob/0840eb69b97617b09aca098f73948c61de563194/LICENSE

export interface Maybe<T extends {}> {
// isJust: () => boolean
// isNothing: () => boolean
map: <U extends {}>(f: (value: T) => U) => Maybe<U>
chain: <U extends {}>(f: (value: T) => Maybe<U>) => Maybe<U>
export interface IMaybe<T extends {}> {
isJust: () => boolean
isNothing: () => boolean
map: <U extends {}>(f: (value: T) => U) => IMaybe<U>
alt: (other: IMaybe<T>) => IMaybe<T>
altLazy: (other: () => IMaybe<T>) => IMaybe<T>
chain: <U extends {}>(f: (value: T) => IMaybe<U>) => IMaybe<U>
orDefault: <U>(defaultValue: U) => T | U
orDefaultLazy: <U>(getDefaultValue: () => U) => T | U
filter: (pred: (value: T) => boolean) => Maybe<T>
filter: (pred: (value: T) => boolean) => IMaybe<T>
extract: () => T | undefined
// extractNullable: () => T | null
extractNullable: () => T | null
ifJust: (f: (value: T) => void) => this
// ifNothing: (f: () => void) => this
ifNothing: (f: () => void) => this
}

export const just = <T extends {}>(value: T): Maybe<T> => {
const instance: Maybe<T> = {
// isJust: () => true,
// isNothing: () => false,
map: (f) => just(f(value)),
export type Maybe<T extends {}> = IMaybe<T>

export const Just = <T extends {}>(value: T): IMaybe<T> => {
const instance: IMaybe<T> = {
isJust: () => true,
isNothing: () => false,
map: (f) => Just(f(value)),
alt: () => instance,
altLazy: () => instance,
chain: (f) => f(value),
orDefault: () => value,
orDefaultLazy: () => value,
filter: (pred) => (pred(value) ? instance : nothing()),
filter: (pred) => (pred(value) ? instance : Nothing),
extract: () => value,
// extractNullable: () => value,
extractNullable: () => value,
ifJust: (f) => (f(value), instance),
// ifNothing: () => instance,
ifNothing: () => instance,
}
if (import.meta.env.DEV) {
Object.freeze(instance)
}
return instance
}

export const nothing = <T extends {}>(): Maybe<T> => {
const instance: Maybe<T> = {
// isJust: () => false,
// isNothing: () => true,
map: () => nothing(),
chain: () => nothing(),
orDefault: (defaultValue) => defaultValue,
orDefaultLazy: (getDefaultValue) => getDefaultValue(),
filter: () => instance,
extract: () => undefined,
// extractNullable: () => null,
ifJust: () => instance,
// ifNothing: (f) => (f(), instance),
}
return instance
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const Nothing: IMaybe<any> = {
isJust: () => false,
isNothing: () => true,
map: () => Nothing,
alt: (other) => other,
altLazy: (other) => other(),
chain: () => Nothing,
orDefault: (defaultValue) => defaultValue,
orDefaultLazy: (getDefaultValue) => getDefaultValue(),
filter: () => Nothing,
extract: () => undefined,
extractNullable: () => null,
ifJust: () => Nothing,
ifNothing: (f) => (f(), Nothing),
}

if (import.meta.env.DEV) {
Object.freeze(Nothing)
}

type MaybeFromNullable = <T extends {}>(value: Nullable<T>) => Maybe<T>
type Nullish = null | undefined
type MaybeFromNullable = <T extends {}>(value: T | Nullish) => IMaybe<T>

export const fromNullable: MaybeFromNullable = (value) => (value != null ? just(value) : nothing())
export const fromNullable: MaybeFromNullable = (value) => (value != null ? Just(value) : Nothing)

type MaybeFromFalsy = <T extends {}>(value: Nullable<T> | false | 0 | 0n | '') => Maybe<T>
type Falsy = Nullish | false | 0 | 0n | ''
type MaybeFromFalsy = <T extends {}>(value: T | Falsy) => IMaybe<T>

export const fromFalsy: MaybeFromFalsy = (value) => (value ? just(value) : nothing())
export const fromFalsy: MaybeFromFalsy = (value) => (value ? Just(value) : Nothing)
14 changes: 6 additions & 8 deletions src/features/editor/codemirror/highlightLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,19 @@ import { type Extension, StateEffect, StateField } from '@codemirror/state'
import { Decoration, type DecorationSet, EditorView } from '@codemirror/view'
import { filterEffects, mapEffectValue, reduceRangeSet } from '@codemirror-toolkit/utils'

import { fromNullable } from '@/common/maybe'
import type { Maybe } from '@/common/maybe'

import { ClassName } from './classNames'
import type { RangeSetUpdateFilter } from './rangeSet'
import { hasNonEmptySelectionAtLine } from './text'

export const HighlightLineEffect = StateEffect.define<{
pos?: number
pos: Maybe<number>
filter?: RangeSetUpdateFilter<Decoration>
}>({
map({ pos: targetPos, filter }, change) {
map({ pos: pos_M, filter }, change) {
return {
pos: fromNullable(targetPos)
.map((pos) => change.mapPos(pos))
.extract(),
pos: pos_M.map((pos) => change.mapPos(pos)),
filter,
}
},
Expand Down Expand Up @@ -54,9 +52,9 @@ const highlightLineField = StateField.define<DecorationSet>({
)
return filterEffects(transaction.effects, HighlightLineEffect).reduce(
(resultDecorations, effect) =>
mapEffectValue(effect, ({ pos: targetPos, filter }) =>
mapEffectValue(effect, ({ pos: pos_M, filter }) =>
resultDecorations.update({
add: fromNullable(targetPos)
add: pos_M
.map((pos) => {
const hasOverlappedSelection = hasNonEmptySelectionAtLine(
transaction.state.doc.lineAt(pos),
Expand Down
22 changes: 9 additions & 13 deletions src/features/editor/codemirror/wavyUnderline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,21 @@ import { type Extension, StateEffect, StateField } from '@codemirror/state'
import { Decoration, type DecorationSet, EditorView } from '@codemirror/view'
import { filterEffects, mapEffectValue } from '@codemirror-toolkit/utils'

import { fromNullable } from '@/common/maybe'
import type { Maybe } from '@/common/maybe'

import { ClassName } from './classNames'
import type { RangeSetUpdateFilter } from './rangeSet'

export const WavyUnderlineEffect = StateEffect.define<{
add?: { from: number; to: number }
range: Maybe<{ from: number; to: number }>
filter?: RangeSetUpdateFilter<Decoration>
}>({
map({ add, filter }, change) {
map({ range: range_M, filter }, change) {
return {
add: fromNullable(add)
.map(({ from, to }) => ({
from: change.mapPos(from),
to: change.mapPos(to),
}))
.extract(),
range: range_M.map(({ from, to }) => ({
from: change.mapPos(from),
to: change.mapPos(to),
})),
filter,
}
},
Expand All @@ -34,11 +32,9 @@ const wavyUnderlineField = StateField.define<DecorationSet>({
const decorations = __decorations.map(transaction.changes)
return filterEffects(transaction.effects, WavyUnderlineEffect).reduce(
(resultDecorations, effect) =>
mapEffectValue(effect, ({ add: addByRange, filter }) =>
mapEffectValue(effect, ({ range: range_M, filter }) =>
resultDecorations.update({
add: fromNullable(addByRange)
.map(({ from, to }) => [markDecoration.range(from, to)])
.extract(),
add: range_M.map(({ from, to }) => [markDecoration.range(from, to)]).extract(),
filter,
}),
),
Expand Down
6 changes: 3 additions & 3 deletions src/features/editor/editorSlice.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { EditorView } from '@codemirror/view'
import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit'

import { fromFalsy, fromNullable } from '@/common/maybe'
import * as Maybe from '@/common/maybe'
import type { SourceRange, Statement } from '@/features/assembler/core'

import { type LineLoc, lineRangesEqual } from './codemirror/text'
Expand Down Expand Up @@ -90,7 +90,7 @@ export const editorSlice = createSlice({
(_: EditorState, view: EditorView) => view.state.doc,
],
(highlightRange, doc) =>
fromNullable(highlightRange).chain((range) => {
Maybe.fromNullable(highlightRange).chain((range) => {
const linePos: number[] = []
const rangeTo = Math.min(range.to, doc.length)
for (let pos = range.from; pos < rangeTo; pos++) {
Expand All @@ -99,7 +99,7 @@ export const editorSlice = createSlice({
linePos.push(line.from)
}
}
return fromFalsy(linePos.length && linePos)
return Maybe.fromFalsy(linePos.length && linePos)
}),
),
selectEditorBreakpoints: (state) => state.breakpoints,
Expand Down
Loading

0 comments on commit bf43881

Please sign in to comment.