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

Ability to opt out of "unsafe" methods #160

Open
pmpfr opened this issue Jan 3, 2018 · 3 comments
Open

Ability to opt out of "unsafe" methods #160

pmpfr opened this issue Jan 3, 2018 · 3 comments

Comments

@pmpfr
Copy link

pmpfr commented Jan 3, 2018

Enum gives you withName which throws, whether you like it or not. It also gives you "safe" versions but it discourages you from using them because they have longer names.

It would be nice to be able to make the opposite choice: to have only the safe methods. E.g.

  def withName(name: String): Try[A]

and variants. Or Option not Try.

People who really want to "force" the Try could call Foo.withName("bar").get which would be visible to e.g. wartremover in user code.

@lloydmeta
Copy link
Owner

It also gives you "safe" versions but it discourages you from using them because they have longer names.

Yeah, this is a good point, but changing the API would:

  • Break compatibility w/ std lib Enumeration. Right you just need to change the declaration of your std lib Enumeration and all your call sites stay the same
  • Break backwards compatibility.

That said, I'm not totally opposed to doing this and in fact would like to, while mitigating the effects of breaking changes.

Currently playing around with the idea of introducing an UnsafeEnum trait that mirrors current behaviour and evolving Enum do what you mention.

Let's brainstorm a few more ideas :)

@pmpfr
Copy link
Author

pmpfr commented Jan 4, 2018

stdlib Enumeration compatibility is something I hadn't appreciated was important, so I'll have to defer to you if my suggestion is unworkable from that point of view. Do I need it without realising it? I think I'm just calling methods on Enum currently as if it was any other standalone userland trait.

Backwards compatibility is definitely important though. If I had a time machine, I might try to summon enough arrogance to argue for changing the interface in the past to what I want now, but that's not what I'm suggesting. The reason I say "opt out" is so that people can keep the API as is (if they actively want to use the throwing methods) or migrate at their leisure if they want to - even one enum at a time.

I'm thinking something like this:

trait SafeEnumApiViaOption[A <: EnumEntry] {
  def withName(name: String): Option[A]
  // other Option returning methods etc but nothing throwing
}

type SafeEnum[A] = SafeEnumApiViaOption[A]

trait UnsafeStdlibCompatApi[A <: EnumEntry] {
  @SuppressWarnings(/*wartremover*/)
  def withName(name: String): A
  // ... everything there is now in the public API, safe and unsafe
}

type Enum[A] = UnsafeStdlibCompatApi[A]

In userland, people can then choose to extend Enum (as they do now and to avoid breaking existing code) or extend SafeEnum.

The indirection through type aliases (and the trait names I've written) are for illustration - you probably wouldn't want that in real life. At this point, I'm leaving open the possibility of letting users have the equivalent of SafeEnumApiViaTry[A], SafeEnumApiViaEither[A] but if that's even a good idea, it would probably be done a different way (e.g. extension trait either in the library or by users).

Clearly you couldn't mix both Enum and SafeEnum together (either for code deduplication in the implementations or in userland) to get "both APIs" because they define same-name methods returning different types. That's sad, but unavoidable I think. If we wanted code deduplication in the implementation, we could only do it via forwarders (which may or may not be worthwhile).

All that said, if you actually are up for evolving the existing Enum trait in a non-backwards compatible way, I'm not going to argue against it...

@lloydmeta
Copy link
Owner

Ah, that sounds like the reverse of what I proposed, which is cool too.

Either way, if we introduce a new trait for enums, we would need to figure out how to evolve the integrations too (which are mostly typed to Enum). Perhaps we will need to introduce a typeclass or something that both Enum and SafeEnum are part of and have instances for both in order to prevent duplication...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants