Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add do notation for Array #2678

Merged
merged 13 commits into from
May 3, 2024
5 changes: 5 additions & 0 deletions .changeset/metal-balloons-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": minor
---

Add do notation for Array
81 changes: 80 additions & 1 deletion packages/effect/src/Array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type { Predicate, Refinement } from "./Predicate.js"
import { isBoolean } from "./Predicate.js"
import * as Record from "./Record.js"
import * as Tuple from "./Tuple.js"
import type { NoInfer } from "./Types.js"
import type { MergeRecord, NoInfer } from "./Types.js"

/**
* @category type lambdas
Expand Down Expand Up @@ -2114,3 +2114,82 @@ export const cartesian: {
2,
<A, B>(self: ReadonlyArray<A>, that: ReadonlyArray<B>): Array<[A, B]> => cartesianWith(self, that, (a, b) => [a, b])
)

// -------------------------------------------------------------------------------------
// do notation
// -------------------------------------------------------------------------------------

/**
* @since 2.4.0
* @category do notation
*/
export const Do: Array<{}> = of({})
KhraksMamtsov marked this conversation as resolved.
Show resolved Hide resolved

/**
* Binds an effectful value in a `do` scope
gcanti marked this conversation as resolved.
Show resolved Hide resolved
*
* @since 2.4.0
* @category do notation
*/
export const bind: {
<N extends string, K, A>(
tag: Exclude<N, keyof K>,
f: (_: K) => Array<A>
): (self: Array<K>) => Array<MergeRecord<K, { [k in N]: A }>>
<K, N extends string, A>(
self: Array<K>,
tag: Exclude<N, keyof K>,
f: (_: K) => Array<A>
): Array<MergeRecord<K, { [k in N]: A }>>
} = dual(3, <K, N extends string, A>(
self: Array<K>,
tag: Exclude<N, keyof K>,
f: (_: K) => Array<A>
): Array<MergeRecord<K, { [k in N]: A }>> =>
flatMap(self, (k) =>
map(
f(k),
(a): MergeRecord<K, { [k in N]: A }> => ({ ...k, [tag]: a } as any)
)))

/**
* @category do notation
* @since 2.4.0
*/
export const bindTo: {
<N extends string>(tag: N): <A>(self: Array<A>) => Array<Record<N, A>>
<A, N extends string>(self: Array<A>, tag: N): Array<Record<N, A>>
} = dual(
2,
<A, N extends string>(self: Array<A>, tag: N): Array<Record<N, A>> => map(self, (a) => ({ [tag]: a } as Record<N, A>))
)

const let_: {
<N extends string, K, A>(
tag: Exclude<N, keyof K>,
f: (_: K) => A
): (self: Array<K>) => Array<MergeRecord<K, { [k in N]: A }>>
<K, N extends string, A>(
self: Array<K>,
tag: Exclude<N, keyof K>,
f: (_: K) => A
): Array<MergeRecord<K, { [k in N]: A }>>
} = dual(3, <K, N extends string, A>(
self: Array<K>,
tag: Exclude<N, keyof K>,
f: (_: K) => A
): Array<MergeRecord<K, { [k in N]: A }>> =>
map(
self,
(k): MergeRecord<K, { [k in N]: A }> => ({ ...k, [tag]: f(k) } as any)
))

export {
/**
* Like bind for values
*
* @since 2.4.0
* @category do notation
*/
let_ as let
}
23 changes: 23 additions & 0 deletions packages/effect/test/Array.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { deepStrictEqual, double, strictEqual } from "effect-test/util"
import * as Util from "effect-test/util"
import * as RA from "effect/Array"
import * as E from "effect/Either"
import { identity, pipe } from "effect/Function"
Expand Down Expand Up @@ -1222,4 +1223,26 @@ describe("ReadonlyArray", () => {
const arr: ReadonlyArray<X> = [{ a: "a", b: 2 }, { a: "b", b: 1 }]
expect(RA.sortWith(arr, (x) => x.b, Order.number)).toEqual([{ a: "b", b: 1 }, { a: "a", b: 2 }])
})

it("Do notation", () => {
const _do = RA.Do
Util.deepStrictEqual(_do, RA.of({}))

const doA = RA.bind(_do, "a", () => ["a"])
Util.deepStrictEqual(doA, RA.of({ a: "a" }))

const doAB = RA.bind(doA, "b", (x) => ["b", x.a + "b"])
Util.deepStrictEqual(doAB, [
{ a: "a", b: "b" },
{ a: "a", b: "ab" }
])
const doABC = RA.let(doAB, "c", (x) => [x.a, x.b, x.a + x.b])
Util.deepStrictEqual(doABC, [
{ a: "a", b: "b", c: ["a", "b", "ab"] },
{ a: "a", b: "ab", c: ["a", "ab", "aab"] }
])

const doABCD = RA.bind(doABC, "d", () => RA.empty())
Util.deepStrictEqual(doABCD, [])
})
})