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

Proposal: Opaque #50

Closed
resynth1943 opened this issue Jul 21, 2019 · 22 comments · Fixed by #53
Closed

Proposal: Opaque #50

resynth1943 opened this issue Jul 21, 2019 · 22 comments · Fixed by #53
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@resynth1943
Copy link
Contributor

resynth1943 commented Jul 21, 2019

Please add support for opaque types, like so (here's a one liner):

type Opaque<V> = V & { readonly __opq__: unique symbol };

This would then be used like so:

type AccountNumber = Opaque<number>;
type AccountBalance = Opaque<number>;

function createAccountNumber (): AccountNumber {
    return 2 as AccountNumber;
}

function getMoneyForAccount (accountNumber: AccountNumber): AccountBalance {
    return 4 as AccountBalance;
}

// CompilerError:
getMoneyForAccount(2); 

// CompilerSuccess
getMoneyForAccount(createAccountNumber());

(copied from my gist)

This would be incredibly useful, and the TypeScript developers just don't seem like they're interested in implementing it.

@sindresorhus
Copy link
Owner

I like it.

This sounds like newtype in Haskell: https://stackoverflow.com/questions/17591276/what-programming-languages-have-something-like-haskell-s-newtype

Relevant reading:

Another version:

type Brand<K, T> = K & { __brand: T }

type USD = Brand<number, "USD">
type EUR = Brand<number, "EUR">

@sindresorhus
Copy link
Owner

Possible names:

  • Opaque
  • Newtype
  • Brand

I prefer Newtype as it’s the most descriptive one.

@resynth1943
Copy link
Contributor Author

I would be happy to send a PR. All good?

@BendingBender
Copy link
Contributor

I'd actually prefer Opaque, it decribes best what this type does for people who don't know Haskell. I find Newtype makes sense for people with Haskell background but what should it mean for someone who doesn't know Haskell? A new type? This could be anything.

@sindresorhus
Copy link
Owner

My problem with Opaque is that it has a different meaning in Swift: https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html

For me, Newtype reads as creating a new type based on the given type.

@resynth1943
Copy link
Contributor Author

Yeah, I'm happy to use that name, since it follows the same concept as my original proposal.

@resynth1943
Copy link
Contributor Author

@BendingBender Yeah, that's a good point. Flow uses opaque to describe an opaque type, so Opaque might be more familiar to people using Flow, or TypeScript.

I do think that people could just check the README to see what the type means, if they're unsure.

@BendingBender
Copy link
Contributor

BendingBender commented Jul 21, 2019

Oh, didn't know about Swift. As far as I understand it, it's not the same in Swift but also not that different. In Swift it hides the specific type returned from a function. This goes a bit in the same direction.

In Haskell, a newtype is also not necessarily an opaque type, it's just a type that must have exactly one type parameter and one constructor. Opaque types in Haskell are types whose constructors are not exposed from a module, this can be newtypes but also plain data types can be opaque.

@BendingBender
Copy link
Contributor

For me, Newtype reads as creating a new type based on the given type.

You're right. But what I'm trying to say is that it doesn't tell the user what's the purpose of this new type. Opaque is a tad clearer on this.

@sindresorhus
Copy link
Owner

I don't feel strongly about it and I'm ok with Opaque too, so we can go for that. PR welcome :)

@sindresorhus sindresorhus added enhancement New feature or request help wanted Extra attention is needed labels Jul 21, 2019
@resynth1943
Copy link
Contributor Author

resynth1943 commented Jul 21, 2019 via email

@resynth1943
Copy link
Contributor Author

@sindresorhus I've sent the Pull Request. #53 👍

@sindresorhus
Copy link
Owner

Exciting => microsoft/TypeScript#33038

@saitonakamura
Copy link

@resynth1943 Sorry for bothering, but I think Opaque is unfortunate name for it. The reason is that Opaque by definition hides the internal data structure (that's why it's opaque) so you can't do anything useful with it without the functions that can extract the underlying data from the opaque type. Much like in languages with classes and private fields you can't access or interact with them unless a class will provide a method for it. Current implementation totally allows to get an access to the underlying structure and even focuses on making it easy (via intersection). I'd say Brand is pretty good name cause I've it used for such in various places of TS community. I'm researching the theory and other terms for it.

@sindresorhus
Copy link
Owner

@saitonakamura What do you think about the name Unique? Seeing as TS intends to use the unique keyword.

@resynth1943
Copy link
Contributor Author

@saitonakamura I'd be interested to hear what you think is a better name for this. I obtained the name "opaque" from Flow, which employs the use of the same name.

@saitonakamura
Copy link

saitonakamura commented Sep 9, 2019

@resynth1943 sorry if it's already clear for you, but for the sake of clarity: the flow opaque type is truly opaque. The same example with the current definition would result in no error (it's kinda hard to implement modules in a playground, but if you wanna play, here is ts playground link)

type Opaque<V> = V & { readonly __opq__: unique symbol };
type NumberAlias = Opaque<number>

function convert(x: NumberAlias): number {
  return x; // No error
}

@sindresorhus , @resynth1943 Unique does sound like a good name to me, exploiting the ts own naming. Brand is commonly (well, as much commonly as other advanced type aspects in TS world) used across articles I've seen in the wild. Another option would be Nominal, but it could be unclear to the person who's not familiar with the structural/nominal typing topic.

Honestly, I don't have any preference over those three, I mean it's just naming, I think as long as it's not confusing it's fine. But if you'll point a gun at my foot and ask to choose one I'd go for Unique.

Let me know what option you're ok with, i can submit a PR if you want

@resynth1943
Copy link
Contributor Author

I'm not sure I know enough about this, but I'm confused as to why Typescript ignores the __opq__ marker in the code you posted above.

@saitonakamura
Copy link

saitonakamura commented Sep 11, 2019

@resynth1943 It would be the same for flow actually
To my understanding, the reason is that functions, by nature, are

  • covariant for arguments (that's why it's not enough to pass a number as an argument, because it doesn't have __opq__ prop), unless specified otherwise (e.g. with exact types it will be invariant)
  • contravariant by return type, in a sense that we can just cut the strange part with __opq__ and leave a number, cause it's a part of intersection

@sindresorhus
Copy link
Owner

I don't think it's worth doing a breaking release now, since TS will get support for this anyway, which would eventually make our type moot.

@saitonakamura
Copy link

Ok, thank you for the discussion 🙂

@resynth1943
Copy link
Contributor Author

I have to say, though, all this is confusing. You would think TypeScript would treat everything the same

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants