Skip to content
This repository was archived by the owner on Sep 20, 2023. It is now read-only.

Add missing Functor instance for Tuple2 #572

Closed

Conversation

Topsii
Copy link

@Topsii Topsii commented Oct 17, 2022

@angerman
Copy link
Contributor

angerman commented Mar 14, 2023

This is required for 9.6.1.

@TristanCacqueray
Copy link

Dear @vincenthz , would you mind doing a new release with this fix to enable basement with ghc-9.6 please.

@erikd
Copy link

erikd commented Mar 29, 2023

Is there any reason why this has not been merged? Is there any help I can offer?

@angerman
Copy link
Contributor

angerman commented May 5, 2023

I doubt this PR has any chance of ever being merged. This change adds a Functor instance that simply makes no sense.
This is a bug in the compiler (well, technically base, but the compiler and base are effectively the same for now), and the compiler must be fixed.

@tomjaguarpaw
Copy link

Why does it make no sense? And how can it have a Bifunctor instance if the Functor instance makes no sense?

@chshersh
Copy link

chshersh commented May 6, 2023

I can understand why adding the Functor instance for Tuple2 might be undesirable in the presence of Bifunctor (fmap implicitly maps over the second argument which might be confusing, and first / second are more explicit here).

I believe it should be possible to implement the Functor instance using custom type errors to disallow the usage of fmap but the Bifunctor instance still should work. If not, I'd be sad 🥲

@tomjaguarpaw
Copy link

it should be possible to implement the Functor instance using custom type errors to disallow the usage of fmap

I don't understand. Isn't the point of this thread that since the quantified constraint was added to Bifunctor you can't have a Bifunctor unless it comes with a Functor instance that agrees with it?

@angerman
Copy link
Contributor

angerman commented May 7, 2023

@tomjaguarpaw the Bifunctor allows you to map of either, or both. This may be desirable. The Functor instance now forces you to allow mapping over only the second component. And it doesn’t allow you to qualify this.

If I have tuples, why does mapping over only the second component make sense all of a sudden? Why not over the first? What makes the second special?

I design a data type, I should be free to chose what instances make sense; but instead here I am forced to provide an instance that I might fundamentally disagree with wrt to my data type. Why provide mapping over only the second component, why not first? Why do I need this if I have bimap, which very explicitly allows me to specify over what I want to map.

I know that from a set theoretic perspective we might argue a tuple is a mapping, but that is a major assumption. Maybe I’m modeling x -> y as (y,x)? And now I’m forced to map over x? Why? Or maybe this is not a mapping in any set theoretic sense anyway?

So the option then is to implement Functor with fmap = undefined or error because we want to prevent people from shooting themselves into their feet by somehow ending up accidentally using the Functor instance which simply should not exist for our data type. Yes it could exist and it kind of exists as part of the bimap definition. But we may have very good reasons not to want to adopt a Functor instance for our Datatype because it simply makes no sense in this specific context.

Maybe the solution instead is to drop the bimap instance.

In any case, lots of unnecessary churn (add or remove instance, just for compatibility; cutting a release, …, downstream bounds adjustments, …) a lot of work for maintainers of this and consuming packages, for what? None of them really reap any benefit from this, but are burdened with a lot of work for virtually no benefit except “compiles with 9.6+ now”. And it’s not like this came with a warning for ~2+ releases that would have shown the real world impact of this change.

I’m digressing. I’ve made my stance on this backwards compatibility, breaking changes topic clear. And I’m actively trying to work towards it.

Edit: updated some spelling mistakes.

@vincenthz
Copy link
Member

as some previously captured here, the whole point of not having Functor was to not have biased implementation available to the user.

This is hugely disappointing again from the ghc / base maintainers to completely change the semantics of a class after multiple major releases, and fly in the face of stability and backwards compatibility that are required to have a decent ecosystem and language.

Ultimately this and the many previous (and likely future) cases of unilateral pointless changes, hurts Haskell adoption to the point that now I don't see a future in this and have actively stirred away from using it (or recommending its use anywhere).

@vincenthz vincenthz closed this May 26, 2023
@sergv
Copy link

sergv commented May 29, 2023

I don't get the argument that fmap f may not make sense for a type that has Bifunctor instance. Suppose that's true, but then completely equivalent bimap id f somehow does make sense?

@erikd
Copy link

erikd commented May 29, 2023

bimap id f is explicit.

fmap f only affects the second value of the tuple which people who do not know Haskell well would find surprising.

@vincenthz
Copy link
Member

@sergv do you get the argument that the class semantic has changed between versions ? Because that's what it is about here. It has a narrower meaning than what it started with.

@tomjaguarpaw
Copy link

bimap id f is explicit. fmap f only affects the second value of the tuple which people who do not know Haskell well would find surprising.

This doesn't hold much weight for me. base, or anyone, could define fmap' = bimap id.

do you get the argument that the class semantic has changed between versions

The underlying semantics haven't changed at all. Exactly the same Bifunctor instances can be provided after as before.

@vincenthz
Copy link
Member

this is factually false @tomjaguarpaw, I cannot provide the bifunctor instance anymore now without adding something I do not want (the Functor instance).

@tomjaguarpaw
Copy link

I guess that's a debate around the semantics of "sematics" then :D

To me "semantics of a type class staying the same" means (roughly) that you can give the same instances with the same implementations. For example, for me, the "semantics" of Monad didn't change when Applicative was required to be a superclass. Under your notion, did the "semantics" of Monad change under those circumstances?

@vincenthz
Copy link
Member

This conversation is turning sterile.

I have no interest in wasting time further on this topic, this specific change broke my use case and change what Bifunctor meant (to me), now I cannot use it.

If you cannot see that it has an impact on the stability of the language/libraries and this (and other kind of similar changes that we endured in the past) are harmful to the language adoption and maintainers, then it's even more lost than I imagined.

@haskell-foundation haskell-foundation locked as resolved and limited conversation to collaborators May 30, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants