-
Notifications
You must be signed in to change notification settings - Fork 299
Explain why Orphan Instances are forbidden #313
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Conversation
language/Type-Classes.md
Outdated
@@ -78,7 +78,7 @@ assert false = fail "Assertion failed" | |||
|
|||
## Orphan Instances | |||
|
|||
Type class instances which are defined outside of both the module which defined the class and the module which defined the type are called *orphan instances*. Some programming languages (including Haskell) allow orphan instances with a warning, but in PureScript, they are forbidden. Any attempt to define an orphan instance in PureScript will mean that your program does not pass type checking. | |||
Type class instances which are defined outside of both the module which defined the class and the module which defined the type are called *orphan instances*. Only one instance is allowed per type and class combination. Orphan instances enable duplicate instances to be defined in separate modules. These modules are fine independently, but if both modules are ever imported into the same project, then an instance collision would break the build. Some programming languages (including Haskell) allow orphan instances with a warning, but in PureScript, they are forbidden. Any attempt to define an orphan instance in PureScript will mean that your program does not pass type checking. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this section:
Only one instance is allowed per type and class combination. Orphan instances enable duplicate instances to be defined in separate modules. These modules are fine independently, but if both modules are ever imported into the same project, then an instance collision would break the build.
The diff makes this difficult to see.
I think the explanation of why they are forbidden should come after the first paragraph, rather than occur within it; we need to wait until the reader knows what an orphan instance is and what will happen if they try to define one before we try to explain why this happens. Also, there's more to it than just the issue that the compiler won't know what to do if two different choices for a particular instance are in scope. For example, banning orphan instances gives us global uniqueness of instances: the property that there can only ever exist one instance for a particular class and type (or set of types, for multi-parameter classes). This property is useful in quite a few places; you may have made use of it unknowingly. One example is of Ord-based maps and sets: if it were possible to insert some values into a map using one Ord instance, and then try to retrieve them using a different one, you'd have keys just disappear from your map. Another example is if you had a type class which defined serialization and deserialization operations: global uniqueness of instances means that you can't serialize something with one instance and then try to deserialize it with a different one. |
The line of reasoning that makes most sense for me is:
But I adjusted this PR based on your feedback. |
language/Type-Classes.md
Outdated
* If it chooses to report an error, it means that any pair of modules which define the same orphan instance can never be used together. | ||
* If it arbitrarily picks one, we won't be able to determine whether `2 <> 3` will evaluate to 5 or 6. This can make it very difficult to ensure that your program will behave correctly! | ||
|
||
Banning orphan instances also ensures global uniques of instances. Without global uniques, you risk operating on data with incompatible instances in different sections of code. For example, in Ord-based maps and sets, if it were possible to insert some values into a map using one `Ord` instance, and then try to retrieve them using a different `Ord` instance, you'd have keys disappear from your map. Another example is if you had a type class which defined serialization and deserialization operations, you'd be able to serialize something with one instance and then try to deserialize it with a different incompatible instance. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh sorry, one last thing - I just noticed "global uniques" versus "global uniqueness". Was that a deliberate change? "global uniqueness" makes more sense to me, personally.
Thanks - there's one more occurrence of "global uniques" in the following sentence, too. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! I'll merge this in a day or so unless we get any other review comments.
No description provided.