Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nine-buses-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": minor
---

added Number.parseInteger
5 changes: 5 additions & 0 deletions .changeset/quiet-tables-listen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": minor
---

Add Either.transposeOption
5 changes: 5 additions & 0 deletions .changeset/tender-kangaroos-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": minor
---

Add `Array.window` function
5 changes: 5 additions & 0 deletions .changeset/yellow-lemons-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": minor
---

Make Runtime.run* apis dual
12 changes: 12 additions & 0 deletions packages/effect/dtslint/Array.tst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,18 @@ describe("Array", () => {
.type.toBe<[[string, ...Array<string>], ...Array<[string, ...Array<string>]>]>()
})

it("window", () => {
// Array
expect(Array.window(strings, 2)).type.toBe<Array<Array<string>>>()
expect(pipe(strings, Array.window(2))).type.toBe<Array<Array<string>>>()
expect(Array.window(2)(strings)).type.toBe<Array<Array<string>>>()

// NonEmptyArray
expect(Array.window(nonEmptyStrings, 2)).type.toBe<Array<Array<string>>>()
expect(pipe(nonEmptyStrings, Array.window(2))).type.toBe<Array<Array<string>>>()
expect(Array.window(2)(nonEmptyStrings)).type.toBe<Array<Array<string>>>()
})

it("reverse", () => {
// Array
expect(Array.reverse(strings)).type.toBe<Array<string>>()
Expand Down
31 changes: 31 additions & 0 deletions packages/effect/src/Array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1969,6 +1969,37 @@ export const chunksOf: {
return []
})

/**
* Creates sliding windows of size `n` from an `Iterable`.
* If the number of elements is less than `n` or if `n` is not greater than zero,
* an empty array is returned.
*
* @example
* ```ts
* import { Array } from "effect"
*
* const numbers = [1, 2, 3, 4, 5]
* assert.deepStrictEqual(Array.window(numbers, 3), [[1, 2, 3], [2, 3, 4], [3, 4, 5]])
* assert.deepStrictEqual(Array.window(numbers, 6), [])
* ```
*
* @category splitting
* @since 3.13.2
*/
export const window: {
(n: number): <A>(self: Iterable<A>) => Array<Array<A>>
<A>(self: Iterable<A>, n: number): Array<Array<A>>
} = dual(2, <A>(self: Iterable<A>, n: number): Array<Array<A>> => {
const input = fromIterable(self)
if (n > 0 && isNonEmptyReadonlyArray(input)) {
return Array.from(
{ length: input.length - (n - 1) },
(_, index) => input.slice(index, index + n)
)
}
return []
})

/**
* Group equal, consecutive elements of a `NonEmptyReadonlyArray` into `NonEmptyArray`s using the provided `isEquivalent` function.
*
Expand Down
36 changes: 36 additions & 0 deletions packages/effect/src/Either.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { TypeLambda } from "./HKT.js"
import type { Inspectable } from "./Inspectable.js"
import * as doNotation from "./internal/doNotation.js"
import * as either from "./internal/either.js"
import * as option_ from "./internal/option.js"
import type { Option } from "./Option.js"
import type { Pipeable } from "./Pipeable.js"
import type { Predicate, Refinement } from "./Predicate.js"
Expand Down Expand Up @@ -966,3 +967,38 @@ export {
*/
let_ as let
}

/**
* Converts an `Option` of an `Either` into an `Either` of an `Option`.
*
* **Details**
*
* This function transforms an `Option<Either<A, E>>` into an
* `Either<Option<A>, E>`. If the `Option` is `None`, the resulting `Either`
* will be a `Right` with a `None` value. If the `Option` is `Some`, the
* inner `Either` will be executed, and its result wrapped in a `Some`.
*
* @example
* ```ts
* import { Effect, Either, Option } from "effect"
*
* // ┌─── Option<Either<number, never>>
* // ▼
* const maybe = Option.some(Either.right(42))
*
* // ┌─── Either<Option<number>, never, never>
* // ▼
* const result = Either.transposeOption(maybe)
*
* console.log(Effect.runSync(result))
* // Output: { _id: 'Option', _tag: 'Some', value: 42 }
* ```
*
* @since 3.14.0
* @category Optional Wrapping & Unwrapping
*/
export const transposeOption = <A = never, E = never>(
self: Option<Either<A, E>>
): Either<Option<A>, E> => {
return option_.isNone(self) ? right(option_.none) : map(self.value, option_.some)
}
29 changes: 29 additions & 0 deletions packages/effect/src/Number.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,35 @@ export const parse = (s: string): Option<number> => {
: option.some(n)
}

/**
* Tries to parse a string into a integer using Number.parseInt.
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
*
* @param string - A string to convert into a number.
*
* @param radix - A value between 2 and 36 that specifies the base of the number in string.
* If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.
* All other strings are considered decimal.
*
* @returns An `Option<number>` containing the parsed integer if successful.
*
* @example
* import { Option } from "effect"
* import { parseInteger } from "effect/Number"
*
* assert.deepStrictEqual(parseInteger("42"), Option.some(42))
* assert.deepStrictEqual(parseInteger("4.2"), Option.some(4))
* assert.deepStrictEqual(parseInteger("abc"), Option.none())
* assert.deepStrictEqual(parseInteger("101", 2), Option.some(5))
*
* @category constructors
* @since 3.14.0
*/
export const parseInteger = (string: string, radix?: number): Option<number> => {
const num = Number.parseInt(string, radix)
return Number.isNaN(num) ? option.none : option.some(num)
}

/**
* Returns the number rounded with the given precision.
*
Expand Down
74 changes: 54 additions & 20 deletions packages/effect/src/Runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,16 @@ export interface RunForkOptions {
* @since 2.0.0
* @category execution
*/
export const runFork: <R>(
runtime: Runtime<R>
) => <A, E>(self: Effect.Effect<A, E, R>, options?: RunForkOptions) => Fiber.RuntimeFiber<A, E> = internal.unsafeFork
export const runFork: {
<R>(
runtime: Runtime<R>
): <A, E>(effect: Effect.Effect<A, E, R>, options?: RunForkOptions | undefined) => Fiber.RuntimeFiber<A, E>
<R, A, E>(
runtime: Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: RunForkOptions | undefined
): Fiber.RuntimeFiber<A, E>
} = internal.unsafeFork

/**
* Executes the effect synchronously returning the exit.
Expand All @@ -94,8 +101,10 @@ export const runFork: <R>(
* @since 2.0.0
* @category execution
*/
export const runSyncExit: <R>(runtime: Runtime<R>) => <A, E>(effect: Effect.Effect<A, E, R>) => Exit.Exit<A, E> =
internal.unsafeRunSyncExit
export const runSyncExit: {
<A, E, R>(runtime: Runtime<R>, effect: Effect.Effect<A, E, R>): Exit.Exit<A, E>
<R>(runtime: Runtime<R>): <A, E>(effect: Effect.Effect<A, E, R>) => Exit.Exit<A, E>
} = internal.unsafeRunSyncExit

/**
* Executes the effect synchronously throwing in case of errors or async boundaries.
Expand All @@ -106,7 +115,10 @@ export const runSyncExit: <R>(runtime: Runtime<R>) => <A, E>(effect: Effect.Effe
* @since 2.0.0
* @category execution
*/
export const runSync: <R>(runtime: Runtime<R>) => <A, E>(effect: Effect.Effect<A, E, R>) => A = internal.unsafeRunSync
export const runSync: {
<A, E, R>(runtime: Runtime<R>, effect: Effect.Effect<A, E, R>): A
<R>(runtime: Runtime<R>): <A, E>(effect: Effect.Effect<A, E, R>) => A
} = internal.unsafeRunSync

/**
* @since 2.0.0
Expand All @@ -126,10 +138,19 @@ export interface RunCallbackOptions<in A, in E = never> extends RunForkOptions {
* @since 2.0.0
* @category execution
*/
export const runCallback: <R>(
runtime: Runtime<R>
) => <A, E>(effect: Effect.Effect<A, E, R>, options?: RunCallbackOptions<A, E> | undefined) => Cancel<A, E> =
internal.unsafeRunCallback
export const runCallback: {
<R>(
runtime: Runtime<R>
): <A, E>(
effect: Effect.Effect<A, E, R>,
options?: RunCallbackOptions<A, E> | undefined
) => (fiberId?: FiberId.FiberId, options?: RunCallbackOptions<A, E> | undefined) => void
<R, A, E>(
runtime: Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: RunCallbackOptions<A, E> | undefined
): (fiberId?: FiberId.FiberId, options?: RunCallbackOptions<A, E> | undefined) => void
} = internal.unsafeRunCallback

/**
* Runs the `Effect`, returning a JavaScript `Promise` that will be resolved
Expand All @@ -142,10 +163,16 @@ export const runCallback: <R>(
* @since 2.0.0
* @category execution
*/
export const runPromise: <R>(
runtime: Runtime<R>
) => <A, E>(effect: Effect.Effect<A, E, R>, options?: { readonly signal?: AbortSignal } | undefined) => Promise<A> =
internal.unsafeRunPromise
export const runPromise: {
<R>(
runtime: Runtime<R>
): <A, E>(effect: Effect.Effect<A, E, R>, options?: { readonly signal?: AbortSignal } | undefined) => Promise<A>
<R, A, E>(
runtime: Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal } | undefined
): Promise<A>
} = internal.unsafeRunPromise

/**
* Runs the `Effect`, returning a JavaScript `Promise` that will be resolved
Expand All @@ -157,12 +184,19 @@ export const runPromise: <R>(
* @since 2.0.0
* @category execution
*/
export const runPromiseExit: <R>(
runtime: Runtime<R>
) => <A, E>(
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal } | undefined
) => Promise<Exit.Exit<A, E>> = internal.unsafeRunPromiseExit
export const runPromiseExit: {
<R>(
runtime: Runtime<R>
): <A, E>(
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal } | undefined
) => Promise<Exit.Exit<A, E>>
<R, A, E>(
runtime: Runtime<R>,
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal } | undefined
): Promise<Exit.Exit<A, E>>
} = internal.unsafeRunPromiseExit

/**
* @since 2.0.0
Expand Down
11 changes: 2 additions & 9 deletions packages/effect/src/internal/configProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ const parseQuotedIndex = (str: string): Option.Option<number> => {
matchedIndex !== undefined && matchedIndex.length > 0 ?
Option.some(matchedIndex) :
Option.none(),
Option.flatMap(parseInteger)
Option.flatMap(number.parseInteger)
)
}
return Option.none()
Expand Down Expand Up @@ -686,20 +686,13 @@ const splitIndexFrom = (key: string): Option.Option<[string, number]> => {
matchedIndex !== undefined && matchedIndex.length > 0 ?
Option.some(matchedIndex) :
Option.none(),
Option.flatMap(parseInteger)
Option.flatMap(number.parseInteger)
)
return Option.all([optionalString, optionalIndex])
}
return Option.none()
}

const parseInteger = (str: string): Option.Option<number> => {
const parsedIndex = Number.parseInt(str)
return Number.isNaN(parsedIndex) ?
Option.none() :
Option.some(parsedIndex)
}

const keyName = (name: string): KeyComponent => ({
_tag: "KeyName",
name
Expand Down
Loading