Skip to content

Commit

Permalink
For Data.TaggedEnum added capitalized constructor naming convention. (
Browse files Browse the repository at this point in the history
#2785)

Co-authored-by: maksim.khramtsov <maksim.khramtsov@btsdigital.kz>
Co-authored-by: Tim <hello@timsmart.co>
  • Loading branch information
3 people committed Jul 8, 2024
1 parent 9fff5da commit f6cbe34
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 26 deletions.
6 changes: 6 additions & 0 deletions .changeset/happy-jeans-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"effect": major
---

For `Data.TaggedEnum` added capitalized constructor naming convention.
Helper functions `$is` and `$match` have been renamed to `is` and `match`.
2 changes: 1 addition & 1 deletion packages/cli/src/internal/prompt/confirm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ function renderSubmission(value: boolean, options: Options) {

function handleRender(options: Options) {
return (_: State, action: Prompt.Prompt.Action<State, boolean>) => {
return Action.$match(action, {
return Action.match(action, {
Beep: () => Effect.succeed(renderBeep),
NextFrame: ({ state }) => renderNextFrame(state, options),
Submit: ({ value }) => renderSubmission(value, options)
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/internal/prompt/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ const defaultLocales: Prompt.Prompt.DateOptions["locales"] = {

function handleRender(options: DateOptions) {
return (state: State, action: Prompt.Prompt.Action<State, globalThis.Date>) => {
return Action.$match(action, {
return Action.match(action, {
Beep: () => Effect.succeed(renderBeep),
NextFrame: ({ state }) => renderNextFrame(state, options),
Submit: () => renderSubmission(state, options)
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/internal/prompt/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type Confirm = Data.TaggedEnum<{
}>
const Confirm = Data.taggedEnum<Confirm>()

const showConfirmation = Confirm.$is("Show")
const showConfirmation = Confirm.is("Show")

const renderBeep = Doc.render(Doc.beep, { style: "pretty" })

Expand Down Expand Up @@ -235,7 +235,7 @@ function renderSubmission(value: string, options: FileOptions) {

function handleRender(options: FileOptions) {
return (_: State, action: Prompt.Prompt.Action<State, string>) => {
return Action.$match(action, {
return Action.match(action, {
Beep: () => Effect.succeed(renderBeep),
NextFrame: ({ state }) => renderNextFrame(state, options),
Submit: ({ value }) => renderSubmission(value, options)
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/internal/prompt/number.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ const initialState: State = {

function handleRenderInteger(options: IntegerOptions) {
return (state: State, action: Prompt.Prompt.Action<State, Number>) => {
return Action.$match(action, {
return Action.match(action, {
Beep: () => Effect.succeed(renderBeep),
NextFrame: ({ state }) => renderNextFrame(state, options),
Submit: () => renderSubmission(state, options)
Expand Down Expand Up @@ -302,7 +302,7 @@ export const integer = (options: Prompt.Prompt.IntegerOptions): Prompt.Prompt<nu

function handleRenderFloat(options: FloatOptions) {
return (state: State, action: Prompt.Prompt.Action<State, number>) => {
return Action.$match(action, {
return Action.match(action, {
Beep: () => Effect.succeed(renderBeep),
NextFrame: ({ state }) => renderNextFrame(state, options),
Submit: () => renderSubmission(state, options)
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/internal/prompt/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ function processNext<A>(state: State, choices: Prompt.Prompt.SelectOptions<A>["c

function handleRender<A>(options: SelectOptions<A>) {
return (state: State, action: Prompt.Prompt.Action<State, A>) => {
return Action.$match(action, {
return Action.match(action, {
Beep: () => Effect.succeed(renderBeep),
NextFrame: ({ state }) => renderNextFrame(state, options),
Submit: () => renderSubmission(state, options)
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/internal/prompt/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ const initialState: State = {

function handleRender(options: Options) {
return (state: State, action: Prompt.Prompt.Action<State, string>) => {
return Action.$match(action, {
return Action.match(action, {
Beep: () => Effect.succeed(renderBeep),
NextFrame: ({ state }) => renderNextFrame(state, options),
Submit: () => renderSubmission(state, options)
Expand Down
24 changes: 17 additions & 7 deletions packages/effect/src/Data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,12 +260,22 @@ export const Structural: new<A>(
* @category models
*/
export type TaggedEnum<
A extends Record<string, Record<string, any>> & UntaggedChildren<A>
A extends
& Record<string, Record<string, any>>
& CapitalConstructorNames<A>
& UntaggedChildren<A>
> = keyof A extends infer Tag ?
Tag extends keyof A ? Types.Simplify<{ readonly _tag: Tag } & { readonly [K in keyof A[Tag]]: A[Tag][K] }>
: never
: never

type CapitalConstructorNames<A> = Record<
Uncapitalize<string>,
keyof A extends infer X extends string
? X extends Uncapitalize<X> ? `Use capitalized constructor name. Did you mean "${Capitalize<X>}"?` : never
: never
>

type ChildrenAreTagged<A> = keyof A extends infer K ? K extends keyof A ? "_tag" extends keyof A[K] ? true
: false
: never
Expand Down Expand Up @@ -336,8 +346,8 @@ export declare namespace TaggedEnum {
readonly [Tag in A["_tag"]]: Case.Constructor<Extract<A, { readonly _tag: Tag }>, "_tag">
}
& {
readonly $is: <Tag extends A["_tag"]>(tag: Tag) => (u: unknown) => u is Extract<A, { readonly _tag: Tag }>
readonly $match: {
readonly is: <Tag extends A["_tag"]>(tag: Tag) => (u: unknown) => u is Extract<A, { readonly _tag: Tag }>
readonly match: {
<
Cases extends {
readonly [Tag in A["_tag"]]: (args: Extract<A, { readonly _tag: Tag }>) => any
Expand All @@ -356,15 +366,15 @@ export declare namespace TaggedEnum {
* @since 3.2.0
*/
export interface GenericMatchers<Z extends WithGenerics<number>> {
readonly $is: <Tag extends Z["taggedEnum"]["_tag"]>(
readonly is: <Tag extends Z["taggedEnum"]["_tag"]>(
tag: Tag
) => {
<T extends TaggedEnum.Kind<Z, any, any, any, any>>(
u: T
): u is T & { readonly _tag: Tag }
(u: unknown): u is Extract<TaggedEnum.Kind<Z>, { readonly _tag: Tag }>
}
readonly $match: {
readonly match: {
<
A,
B,
Expand Down Expand Up @@ -482,9 +492,9 @@ export const taggedEnum: {
} = () =>
new Proxy({}, {
get(_target, tag, _receiver) {
if (tag === "$is") {
if (tag === "is") {
return Predicate.isTagged
} else if (tag === "$match") {
} else if (tag === "match") {
return taggedMatch
}
return tagged(tag as string)
Expand Down
22 changes: 11 additions & 11 deletions packages/effect/test/Data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,10 @@ describe("Data", () => {
InternalServerError: { reason: string }
}>
const {
$is,
$match,
InternalServerError,
NotFound
NotFound,
is,
match
} = Data.taggedEnum<HttpError>()

const a = NotFound()
Expand All @@ -206,9 +206,9 @@ describe("Data", () => {
expect(Equal.equals(a, b)).toBe(false)
expect(Equal.equals(b, c)).toBe(true)

expect($is("NotFound")(a)).toBe(true)
expect($is("InternalServerError")(a)).toBe(false)
const matcher = $match({
expect(is("NotFound")(a)).toBe(true)
expect(is("InternalServerError")(a)).toBe(false)
const matcher = match({
NotFound: () => 0,
InternalServerError: () => 1
})
Expand All @@ -227,7 +227,7 @@ describe("Data", () => {
interface ResultDefinition extends Data.TaggedEnum.WithGenerics<2> {
readonly taggedEnum: Result<this["A"], this["B"]>
}
const { $is, $match, Failure, Success } = Data.taggedEnum<ResultDefinition>()
const { Failure, Success, is, match } = Data.taggedEnum<ResultDefinition>()

const a = Success({ value: 1 }) satisfies Result<unknown, number>
const b = Failure({ error: "test" }) satisfies Result<string, unknown>
Expand All @@ -247,27 +247,27 @@ describe("Data", () => {
const bResult = Failure({ error: "boom" }) as Result<string, number>

assert.strictEqual(
$match(aResult, {
match(aResult, {
Success: (_) => 1,
Failure: (_) => 2
}),
1
)
const result = pipe(
bResult,
$match({
match({
Success: (_) => _.value,
Failure: (_) => _.error
})
)
result satisfies string | number
assert.strictEqual(result, "boom")

assert($is("Success")(aResult))
assert(is("Success")(aResult))
aResult satisfies { readonly _tag: "Success"; readonly value: number }
assert.strictEqual(aResult.value, 1)

assert($is("Failure")(bResult))
assert(is("Failure")(bResult))
bResult satisfies { readonly _tag: "Failure"; readonly error: string }
assert.strictEqual(bResult.error, "boom")
})
Expand Down

0 comments on commit f6cbe34

Please sign in to comment.