-
Notifications
You must be signed in to change notification settings - Fork 10
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
Investigate feasability of replacing Type._$cast<T, U> with T & U #59
Comments
I investigated this a little bit and the equivalence seems definitely false - still may be opportunities to improve performance though, so I'll leave this open. |
Couldn't you just do |
Counterexample: T1 = Type._$cast<string, `0x${string}`> // `0x${string}`
T2 = Extract<string, `0x${string}`> // never
|
@MajorLift thanks for your reply. I was picturing the type arguments being swapped in Another option would be to embed the constraint into initial encoding directly, like this. Doing it that way lets you access the property with constraints already applied (so there's no need to cast at all). |
@ahrjarrett Regarding your playground link:
Regarding
type T1 = Type._$cast<boolean, true> // true
type T2 = Type._$cast<true, boolean> // true
type T3 = Type._$cast<string, `0x${string}`> // `0x${string}`
type T4 = Type._$cast<`0x${string}`, string> // `0x${string}`
type T5 = Type._$cast<number, 0> // 0
type T6 = Type._$cast<0, number> // 0
type T7 = Extract<boolean, true> // true
type T8 = Extract<true, boolean> // true
type T9 = Extract<string, `0x${string}`> // never
type T10 = Extract<`0x${string}`, string> // `0x${string}`
type T11 = Extract<number, 0> // never
type T12 = Extract<0, number> // 0 |
@ahrjarrett I applied your suggestion to a new HKT contribution from our latest PR: playground link. Hopefully this example makes my explanation easier to understand.
|
Hey @MajorLift, thank you again for the detailed response. To clarify, my comment about I'm not as familiar with hkt-toolbelt's internals, and so I can't speak to your use case exactly, but it's worth clarifying that To illustrate what I mean, consider a version of type Extract<left, right> = [left] extends [right] ? left : never If Extract only applied to unions, then this type would have no effect. But here you can see** that it does in fact fix the type errors in the example you shared. Granted,
|
Also, I'd love to take you up on the challenge to implement |
|
I think my attempt to be explicit actually introduced ambiguity here 😅 so just to make sure we're on the same page: the reason I made You can test that, if you're skeptical, by removing the custom definition of Thinking about |
As we've established, Is there a specific benefit from using Given that we would never want |
Ah, that must be where we're missing each other -- I didn't realize that was the behavior you wanted. No worries :) my goal in suggesting it was simply because it seemed like a readability win, which seemed to be what Mike was wanting. As far as fold goes, is this what you had in mind? https://tsplay.dev/WkqRpN |
Thought about this a little more -- if y'all do decide to go this route, it might simplify things if you:
FWIW, the reason I'm taking an interest is because I think it would be a big win for new users. But from an accessibility standpoint, my feedback would be that we're asking new users for too much, too soon. |
| @poteat Does this sound like something you might be open to? I'm down; any improvements to readability are well worth it. On the other hand, I think visual distinguishment makes the more complicated subroutines more readable - as well, I aesthetically dislike only having a Levenstein distance of 'one' between modules; I feel like it introduces the chance of typos / misunderstandings. As a middle ground, I would support |
@poteat I agree with the point about visual distinction, and don't feel good about using camelCase for type names for the same reason, especially since we support subpath imports. As I understand it, @ahrjarrett's suggestion is that we replace all of the Perhaps adding a brief section to the readme explaining the difference between our curried HKTs and uncurried generics might be a more straightforward solution? This might be an update we would want to make in any case since we do deploy the uncurried generics. |
@ahrjarrett That's a cool solution! Here are the equivalent implementations using hkt-Toolbelt for comparison: https://tsplay.dev/w2gdYN. Some observations:
|
Hey @MajorLift, thanks for taking a look.
I included the partial application examples not because I think it's a strength of the encoding per se (I actually wasn't sure if it would work until I created the sandbox). The point I was trying to make was that you get a lot of things for free when you have the ability to bind and rebind type parameters. So in the examples I shared, I bind each type parameter twice (and only twice):
The Like you alluded to before, both are valid solutions -- they just come with different tradeoffs. |
@ahrjarrett Both binding and rebinding sound like basic capabilities of hkt-Toolbelt, if I'm understanding you correctly:
I would encourage you to look into implementing arbitrary function composition. It would be interesting to see how you decide to handle the complexity that arises from handling arbitrary arity, which is avoidable when working with curried, unary functions. |
This would be a big win in efficiency, readability, and so if this equivalence is true.
The text was updated successfully, but these errors were encountered: