Comments (16)
The majority of dual signatures are in this form I think:
The majority of our signatures are re-exported from internal implementation, which use the two dual
generics.
from effect.
If you want to play around, you can update the dual signature in the Function module, run pnpm check
and see how it will affect the codebase.
from effect.
The definition of f
should be:
type f_data_first = (a: number, b: number) => number
type f_data_last = (b: number) => (a: number) => number
type f = f_data_last & f_data_first
from effect.
Yeah sorry, mistake with copy/paste. Edited the issue. The points are still valid though, since I tested them with the correct f
definition.
Here's a playground repro: Playground Link
from effect.
from effect.
You're supposed to declare the implementation and the complete signature as the generics: Playground Link
That is: dual<ImplementationSignature, CompleteSignatureIncludingImplementationSignature>(arity, implementation)
from effect.
You're supposed to declare the implementation and the complete signature as the generics: Playground Link
That is:
dual<ImplementationSignature, CompleteSignatureIncludingImplementationSignature>(arity, implementation)
it's more verbose for no valid reason imho, dual is supposed to be almost an internal utility
from effect.
You're supposed to declare the implementation and the complete signature as the generics: Playground Link
That is:
dual<ImplementationSignature, CompleteSignatureIncludingImplementationSignature>(arity, implementation)
Ok gotcha. This would require a rewrite of the majority of our api signatures, so this won't be adopted.
from effect.
The majority of dual signatures are in this form I think:
const f: f = dual(2, (a: number, b: number) => a + b)
Example from Array
:
export const map: {
<S extends ReadonlyArray<any>, B>(
f: (a: ReadonlyArray.Infer<S>, i: number) => B
): (self: S) => ReadonlyArray.With<S, B>
<S extends ReadonlyArray<any>, B>(self: S, f: (a: ReadonlyArray.Infer<S>, i: number) => B): ReadonlyArray.With<S, B>
} = dual(2, <A, B>(self: ReadonlyArray<A>, f: (a: A, i: number) => B): Array<B> => self.map(f))
This would require no change:
export const dual: {
<const Impl extends (...args: readonly any[]) => any, Signature extends Impl = Impl>(
arity: Parameters<Impl>["length"],
body: Impl
): Signature
<Impl extends (...args: readonly any[]) => any, Signature extends Impl = Impl>(
// eslint-disable-next-line @typescript-eslint/unified-signatures
isDataFirst: (args: IArguments) => boolean,
body: Impl
): Signature
} = effect_dual
export { dual as Function_dual }
export const map: {
<S extends ReadonlyArray<any>, B>(
f: (a: Array.ReadonlyArray.Infer<S>, i: number) => B
): (self: S) => Array.ReadonlyArray.With<S, B>
<S extends ReadonlyArray<any>, B>(self: S, f: (a: Array.ReadonlyArray.Infer<S>, i: number) => B): Array.ReadonlyArray.With<S, B>
} = dual(2, <A, B>(self: ReadonlyArray<A>, f: (a: A, i: number) => B): Array<B> => self.map(f))
Of course it's not perfect, I mean there are still cases where you can mess up the implementation signature with the actual dual signature, but should help prevent errors in changing the implementation while not changing the signature or viceversa both for effect codebase and other libs.
The only cases where a manual change would be required is where the dual generics are explicitly set. Still, they should always pop up as compiler error, so would be easy to adjust them I think?
from effect.
You're supposed to declare the implementation and the complete signature as the generics: Playground Link
That is: dual<ImplementationSignature, CompleteSignatureIncludingImplementationSignature>(arity, implementation)
This is only required if generics are explicitly set on the dual
call already. If not, the "normal" way works as before. No changes required. My wording of "you're supposed to" was indeed a bit misleading.
from effect.
We could work around that changing the new dual
signature to allow for the "other" signature first, and real impl later. Same principle should apply, but no internal change in signatures should be needed:
export const dual: {
<Other extends (...args: readonly any[]) => any, Impl extends (...args: readonly any[]) => any, Signature extends Impl = Impl & ([readonly any[]] extends [Parameters<Other>] ? unknown : Other )>(
arity: Parameters<Impl>["length"],
body: Impl
): Signature
<Other extends (...args: readonly any[]) => any, Impl extends (...args: readonly any[]) => any, Signature extends Impl = Impl & ([readonly any[]] extends [Parameters<Other>] ? unknown : Other )>(
// eslint-disable-next-line @typescript-eslint/unified-signatures
isDataFirst: (args: IArguments) => boolean,
body: Impl
): Signature
} = effect_dual
export { dual as Function_dual }
(haven't battle tested it yet)
from effect.
Just for info, it works 90% of the cases, there are a couple of points where it breaks due to being inferred as any
before, which is not possible anymore. I tried working around it but no luck. One can think about changing those places to an explicit type signature in the dual
generics, since there are not that many. Before I dive in, would be cool to have something like 20 functions using dual
being changed in a pr to make it so they have precise types specified in the dual signature instead of it being inferred as the generic function with any
? I can submit it if so.
from effect.
Example from Array.split
:
export const split: {
(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) => { // this is (...args: any[]) => any really, that's how it works now
const input = fromIterable(self)
return chunksOf(input, Math.ceil(input.length / Math.floor(n)))
})
This is how I'd solve it:
export const split: {
(n: number): <A>(self: Iterable<A>) => Array<Array<A>>
<A>(self: Iterable<A>, n: number): Array<Array<A>>
} = dual<
(n: number): <A>(self: Iterable<A>) => Array<Array<A>>,
<A>(self: Iterable<A>, n: number): Array<Array<A>>
>(2, <A>(self: Iterable<A>, n: number) => { // this is (...args: any[]) => any really, that's how it works now
const input = fromIterable(self)
return chunksOf(input, Math.ceil(input.length / Math.floor(n)))
})
eventually making it so they're not duplicated:
declare namespace split {
type DataLast = (n: number): <A>(self: Iterable<A>) => Array<Array<A>>
type DataFirst = <A>(self: Iterable<A>, n: number): Array<Array<A>>
interface split extends DataLast, DataFirst {}
}
export const split: split.split = dual<
split.DataLast,
split.DataFirst
>(2, <A>(self: Iterable<A>, n: number) => { // this is (...args: any[]) => any really, that's how it works now
const input = fromIterable(self)
return chunksOf(input, Math.ceil(input.length / Math.floor(n)))
})
from effect.
declare namespace split {
type DataLast = (n: number): <A>(self: Iterable<A>) => Array<Array<A>>
type DataFirst = <A>(self: Iterable<A>, n: number): Array<Array<A>>
interface split extends DataLast, DataFirst {}
}
export const split: split.split = dual<
split.DataLast,
split.DataFirst
>(2, <A>(self: Iterable<A>, n: number) => { // this is (...args: any[]) => any really, that's how it works now
const input = fromIterable(self)
return chunksOf(input, Math.ceil(input.length / Math.floor(n)))
})
would be problematic in the re-exports as we can't export internal types
from effect.
An alternative could be to reuse the term type directly, since its type is already declared. No duplicates and no issues so far, and you can even omit types for the passed implementation, since it's already 1:1 inferred by the generic used as the DataFirst
parameter.
export const split: {
(n: number): <A>(self: Iterable<A>) => Array<Array<A>>
<A>(self: Iterable<A>, n: number): Array<Array<A>>
} = dual<typeof split, <A>(self: Iterable<A>, n: number) => Array<Array<A>>>(2, (self, n) => {
return [[]]
})
even with more signatures:
export const findLast: {
<A, B>(f: (a: NoInfer<A>, i: number) => Option.Option<B>): (self: Iterable<A>) => Option.Option<B>
<A, B extends A>(refinement: (a: NoInfer<A>, i: number) => a is B): (self: Iterable<A>) => Option.Option<B>
<A>(predicate: (a: NoInfer<A>, i: number) => boolean): (self: Iterable<A>) => Option.Option<A>
<A, B>(self: Iterable<A>, f: (a: A, i: number) => Option.Option<B>): Option.Option<B>
<A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Option.Option<B>
<A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Option.Option<A>
} = dual<typeof findLast, <A>(self: Iterable<A>, f: ((a: A, i: number) => boolean) | ((a: A, i: number) => Option.Option<A>)) => Option.Option<A>>(
2,
(self, f) => {
return Option.none()
}
)
Given this change, the generic parameter for DataLast
is better to be called Other
or something, since it contains all the signatures except (or including, no problems with including) the actual implementation (which is DataFirst
, but should be called Impl
really).
export const dual: {
<
Other extends (...args: Array<any>) => any,
Impl extends (...args: Array<any>) => any,
Signature extends Impl = Impl & ([ReadonlyArray<any>] extends [Parameters<Other>] ? unknown : Other)
>(
arity: Parameters<Impl>["length"],
body: Impl
): Signature
<
Other extends (...args: Array<any>) => any,
Impl extends (...args: Array<any>) => any,
Signature extends Impl = Impl & ([ReadonlyArray<any>] extends [Parameters<Other>] ? unknown : Other)
>(
isDataFirst: (args: IArguments) => boolean,
body: Impl
): Signature
} = effect_dual
If you all are ok with this change I can go ahead and submit the PR with the ~20 affected functions changed this way.
Here's the playground for reference: Playground Link
from effect.
I submitted the #3068 pr to see if this can land safely without any disruptions.
from effect.
Related Issues (20)
- Add a drizzle-schema package HOT 5
- Idea: Schema variants HOT 3
- Weblock support in browser platform package HOT 2
- From Discord: Is Option.firstSomeOf expected to work with an array of Data.taggedEnum<AlgebraicDataType?
- Schema.TaggedError doesn't have nicely formatted cause when logged to console (unlike Data.TaggedError)
- Add new Order.localeString ordering option
- Predicate.or should handle refinements as Predicate.and does
- Logged errors only show the first child cause instead of displaying error causes recursively
- RPC File support
- From Discord: Confusion Over Multiple Documentation Versions for Effect-TS API
- From Discord: Schema base64 string -> utf8 string -> json parse
- Suggestion: improve DX of type errors from inside `pipe` and `flow`
- From Discord: Accumulating Incomplete JSON Chunks from a ReadableStream
- Enhance sum type workflow (Data.taggedEnum)
- Cause.UnknownException causes exception when using Bun
- JSONSchema.make does not have same behaviour as JSONSchema.to => erasing properties & not working for some Schema types HOT 7
- Effect.Tag with non Effect-returning functions
- From Discord: TypeScript Type Incompatibility Issue with Generic Schema and Optional Values
- Move type helpers from nested namespace to the top-level namespace HOT 4
- From Discord: decode error which should be caught at compile time
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from effect.