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

Remove the ref equality check in the Eq Array instance #187

Merged
merged 1 commit into from
Jun 23, 2019
Merged

Remove the ref equality check in the Eq Array instance #187

merged 1 commit into from
Jun 23, 2019

Conversation

LiamGoodacre
Copy link
Member

See discussion here: 3b0d306

@fehrenbach
Copy link

Have we really determined that "=== implies ==" does not follow from the laws? And if so, could we change that?

Isn't @hdgarrood's example (reproduced below) ultimately because Eq Number is not law-abiding?

let x = [NaN]
x === x; // true

@hdgarrood
Copy link
Contributor

I think "=== implies ==" does follow from the Eq laws. (Well, at least, seems like it ought to.)

This example is indeed ultimately due to Eq Number not being law-abiding, yes. I'm still in favour of reverting this change, though, because it's easy enough to opt in to this behaviour in the cases where you do really need it; for behaviour which is potentially dangerous like this is, I think having to opt in is the better approach.

It is of course unfortunate that Eq Number is not law abiding but I think this is better than the alternative of having NaN compare equal to itself, because this would be a divergence from IEEE 754 (which I think most programmers are used to), and you'd get some (arguably) even more nonsensical results such as asin 2 comparing equal to 1/0 - 1/0.

@fehrenbach
Copy link

fehrenbach commented Oct 7, 2018

it's easy enough to opt in to this behaviour in the cases where you do really need it;

It is if you have two arrays to compare, but not if something else uses arrays under the hood.
(Compare to how the non-law-abiding Eq instance for Number is the default. It is inconvenient to have to newtype-wrap things.)

for behaviour which is potentially dangerous like this is, I think having to opt in is the better approach.

So where do you draw the line for "potentially dangerous"? Presumably you're not saying that nobody should ever rely on any typeclass law. (edit: this is not fair, I know you're not saying that, and I know that we are in the realm of judgment calls where a bright red line might not exist, but still...) What's different here?

I guess "it's only about performance" is one answer to this.

It does not seem fair to me that a law-abiding optimization is too "potentially dangerous" to be the default, whereas a know to be broken instance is the default. I'm not advocating to get rid of Eq Number, it is useful, but arrays should not have to pay for it.

@hdgarrood
Copy link
Contributor

hdgarrood commented Jan 13, 2019

It is if you have two arrays to compare, but not if something else uses arrays under the hood.

Correct, but if something else uses arrays under the hood, then it ought to be the responsibility of that other thing to opt in to these unsafe referential equality checks, surely?

What's different here?

It's that a property of the language — namely, referential transparency — is being violated. It's not just that you'll end up with incorrect behaviour, it's that you get incorrect behaviour which the language advertises itself as being able to prevent. I think this puts us into a different category than just encountering incorrect behaviour which arises from an unlawful instance (and, while wrong, at least doesn't violate a property that the language is advertised as having).

@hdgarrood
Copy link
Contributor

Actually having thought through it a little more, I don't think it's accurate to say it's just Eq Number which has caused this: I think downstream users should also be able to write unlawful instances without causing language properties to disappear. See @MatthiasHu's example from the discussion in that commit:

module Main where

import Prelude
import Effect
import Effect.Console

data MyVal = MyVal

instance myValEq :: Eq MyVal where
  eq _ _ = false

main :: Effect Unit
main = log $ show $ vs == vs
  where vs = [ MyVal, MyVal ] 

Of course, the oddity with Eq Number means that it's much easier to run into this issue via Number, and that is a large factor in my being in favour of reverting this change, though.

@fehrenbach
Copy link

As long as you only use the safe subset of PureScript referential transparency should hold. JavaScript's === violates referential transparency because the typical notion of value for a functional programming language says nothing about object identity. FFI use is unsafe. I believe so far we agree.

I object to expecting language properties to hold in the presence of unsafe use. I think code should be allowed to use whatever "unsafe" hacks it wants, as long as the interface to the outside world is preserved. For me this interface includes instance laws and other additional requirements that are not checked by the language (like unsafeFreeze's "do not mutate afterwards").

Ugh, I sound like a C person. Undefined behavior for more optimizations! If the programmer isn't careful they get what they deserve! ;) So yeah, maybe you are right. It's hard to admit though. I do think that good things like purity and typeclass laws should allow us to take shortcuts.

@hdgarrood
Copy link
Contributor

I object to expecting language properties to hold in the presence of unsafe use. [...] For me this interface includes instance laws and other additional requirements that are not checked by the language (like unsafeFreeze's "do not mutate afterwards").

Ok, yes, you're absolutely right there. unsafePerformEffect is another example.

@fehrenbach
Copy link

I was just about to say do what you think is best (not that you need my permission — just saying I'd stop complaining...) when I had this terrible idea:

We could use instance chains to select the deep comparison for Eq (Array Number), the one unlawful prelude-defined Eq instance, and otherwise use the shortcut.

(Yes, I know, I'll see myself out.)

@hdgarrood
Copy link
Contributor

Sort of related: https://www.reddit.com/r/haskell/comments/4ivvge/why_no_reference_equality/

Anyway are there any objections to merging this and making a patch-level release?

@hdgarrood hdgarrood merged commit e3504b2 into purescript:master Jun 23, 2019
@LiamGoodacre LiamGoodacre deleted the fix/remove-array-ref-eq branch June 23, 2019 22:31
dstevenson1 added a commit to dstevenson1/purescript-prelude that referenced this pull request Sep 27, 2022
  - This reverts the change in purescript#187
  - Provides a major optimization for array equality checking.
  - Opting into this functionality within all nested records at the
    moment seems unrealistic compared to just restoring this here.

Reviewed By:
dstevenson1 added a commit to dstevenson1/purescript-prelude that referenced this pull request Sep 27, 2022
  - This reverts the change in purescript#187
  - Provides a major optimization for array equality checking.
  - Opting into this functionality within all nested records at the
    moment seems unrealistic compared to just restoring this here.

Reviewed By: pbrant
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

Successfully merging this pull request may close these issues.

3 participants