-
Notifications
You must be signed in to change notification settings - Fork 15
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
Add Foldable / Traversable instances for tuples #206
Comments
Although I appreciate the push for greater consistency I am very skeptical about this. The proposed |
I wonder what the potential breakage from removing tuple instances of |
There is nothing wrong with Similarly one can argue that Removing --- a/libraries/base/Data/Traversable.hs
+++ b/libraries/base/Data/Traversable.hs
@@ -314,8 +314,8 @@ instance Traversable (Either a) where
deriving instance Traversable Solo
-- | @since 4.7.0.0
-instance Traversable ((,) a) where
- traverse f (x, y) = (,) x <$> f y
+-- instance Traversable ((,) a) where
+-- traverse f (x, y) = (,) x <$> f y
-- | @since 2.01
instance Ix i => Traversable (Array i) where results in failure to compile GHC:
I actually was expecting the newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }
instance (Traversable f) => Traversable (WriterT w f) where
traverse f = fmap WriterT . traverse f' . runWriterT where
f' (a, b) = fmap (\ c -> (c, b)) (f a) -- strict first @(,) f so they inline |
|
Hello there. I'm working on codebase with high usage of high order functions that do something with monad M. These helpers change some environment for inner computation, catch errors and perform other complicated actions. A lot of them use tuples to return some additional stuff, so they may have such types:
I didn't see more than 3 return values, however I can't say they can't be found in the future. I had to write a wrapper monad under M, let's say
I need to use This helper should treat state carefully and perform some steps to fill
edit: I started to use my helper function in the codebase, and, after hard work, I updated all the subsystem of my interest to my new monad. Updated and found that
Thanks to everyone who participated in the discussion 🙏. |
I guess you mean
I'm not sure if you're saying you're stuck, but if so I see two possible ways to proceed:
|
I understand that there are low-tech workarounds, that doesn't allow me to use one function in all cases, however I want to give you a motivational example about what may happen in the wild. I've never had no desire to use |
Okay. Once again, I think that if users need |
@tomjaguarpaw could you possibly unpack this argument? What's exactly confusing? As we discussed at https://discourse.haskell.org/t/in-retrospect-was-burning-bridges-a-mistake/8188/46, it's not like |
I mean that
But sadly all three do pertain. Let's not make things worse by allowing I disagree that (I agree that |
What about |
There's a difference of degree. |
I don't see what's confusing about length for tuples retuning 1. It may be surprising for someone coming, say, from Python, but it's a question of adjusting the mental model to properly account for Foldable semantics. And using length without understanding Foldable is a footgun in itself to begin with (arguably, the length function is itself a footgun, you don't know its complexity unless you know for a fact how a type in question implements it, and it's generally a bad idea to use it polymorphically for that exact reason). I see nothing wrong with the proposal. |
My impression is that if we reject this proposal on the grounds of "Traversable for Tuples is confusing", then we are obliged to do something about it, as in: deprecate and remove the instance. If we don't agree on removal, we should accept this proposal. "Let's do nothing" seems like the worst of all options. |
If you start removing things on the grounds that they "are confusing" (which is an extremely subjective metric!) despite being well-defined and internally consistent, I think that would be an indicator that something went horribly wrong somewhere. Case in point, monads are generally quite confusing to a lot of people. Hopefully you see my point. |
"Monads" and " And to be clear: there's nothing wrong with |
Not true at all. I've taught Haskell for a few years to undergrads, and the amount of code I've seen that misuses and abuses monads due to that exact confusion would surprise you. At least, it surprised me. That included a decent few cases of "subtle runtime bugs" as well. So I don't see a substantial difference here.
You could call it Now, one needs to understand what a foldable functor is to understand what it does, which is not particularly helpful to complete beginners -- that much is obvious. However, if you're concerned about that, the only option that would actually help with that IMO is to export a monomorphised version from |
I'm personally not buying into the confusion argument. I use fmap on 2-Tuples a lot and yes, there have been experienced professional Haskellers that got thrown off by it. But once you look at it, there's no other way to define it. What would be confusing is if there were a lot of other definitions of So the confusion is more about "I didn't think about this before". That is an indication that we need better documentation. If we had better mechanisms to selectively import instances, then I won't mind making it harder to import these more exotic instances. |
Without examples it's hard to perform a "differential diagnosis" and determine whether "monads" really are confusing in the "same way" as " However, I do know from long experience that
https://x.com/l7r7_/status/1534992482966159388
https://x.com/ChristiaanBaaij/status/1301769050872315904
https://x.com/g_lebec/status/1607881099308568587
https://x.com/Profpatsch/status/1450470441810812934
https://x.com/Profpatsch/status/1450470441810812934
https://x.com/dfordivam/status/951609307237249024
But there are other definitions that make sense! None of them have type If The fact that there is only one value of type |
I actually don't care about But I really want to have |
I'm not sure what to make of this. It seems you're not really arguing against this proposal, but to rename a member of the Foldable class (which would be an insanely breaking change). In the context of the Foldable class, there are no meaningful alternative definitions of "All instances follow naive intuition" is a pretty hard property to satisfy. We'll be redesigning lots of classes if we go for this. IMO, this is what you get with ad-hoc polymorphism. I believe this is a use case for linters. They may very well emit a fat warning for these exotic instances. |
I agree this is useful, but actually I find
What I am arguing is that we should never have had all of these conditions pertain at the same time
Sadly they do pertain, and indeed changing any one of them would be an "insanely breaking change", so I'm not suggesting changing them. I am arguing that we shouldn't make things worse by adding
I don't think I'm arguing for that. People are welcome to define whatever classes and instances they like in their own packages. I'm even flexible regarding what classes and instances are defined in
Yes, sadly. I believe we should be extremely judicious about what ad hoc polymorphism is exposed from the
I think that's a reasonable counterpoint, and indeed stan does warn when |
I guess you're free to follow that principle and there's not much point in discussing that. But I want to point out that under that definition of "confusing", I find Monad instance for |
OK, fine. Maybe being "confusing" is not sufficient justification. How about "prone to subtle, hard-to-diagnose bugs"? |
Quite interestingly, Elixir makes a difference according to the data structure:
https://hexdocs.pm/elixir/lists-and-tuples.html#size-or-length If we are unwilling to give |
Yeah. I think there are all sorts of things where at least a certain class of users want some kind of warnings for purposes of "questionable ergonomics". I count partial functions and exotic instances both in that category, as well as GHC warnings seem a bit radical (that's why I was opposed to head/tail warnings). Banishing them from base seems harsh, especially when they can serve advanced use cases. And then the only thing left is indeed linters or making it harder to import an identifier. |
Not really evidence per se, but from memory,
This isn't really evidence in favour of your position. It still would've been a bug if we had
I don't see how adding I'll reiterate, adding So, aside from the unproductive "let's do nothing and never speak of this again", I see only two meaningful options in the context of this proposal:
I'm personally in favour of the latter, but I could live with the former. |
I rescind my former claim that my objection to this proposal rests on it being "confusing". Thanks to dialogue with @lierdakil and @hasufell I realise that word suggests that the case is far weaker than it actually is. Instead I'd like to restate the same case but with "prone to subtle, hard-to-diagnose bugs" in place of "confusing". I apologise for the confusion. I'd be grateful if future replies address that objection, rather than the objection of being "confusing".
Thanks, I think these are indeed examples that are to some degree analogous to the instance under discussion here. My personal point of view is that instance (Monoid a, Monoid b, Monoid c, Monoid d) => Monad ((,,,,) a b c d) nor do I think we should add it (this family of instances is only "consistent" until 4-tuples)! I am also doubtful about
Let's look at this through the lens of my upgraded objection: by my metric would this be a source of subtle, hard-to-diagnose bugs? Yes, I think it would. In fact I think having something called
Let's look at this through the lens of my upgraded objection: does adding more potential sources of subtle, hard-to-diagnose bugs make things significantly worse? It seems to me that the answer is obviously yes, unless there is some associated benefit that outweighs the increase in bugs. I have not yet seen that benefit.
I agree. This is one of the observations that made it clear to me that I should upgrade my objection from "confusing", which was a poor choice of word on my part, to "source of subtle, hard-to-diagnose bugs", so thank you for that.
I don't understand why that is "unproductive". It seems to me perfectly productive to keep out a source of subtle, hard-to-diagnose bugs from the standard library. Could you elaborate? |
Taking this position results in not producing anything 🤷 There are issues. The "productive" position is to do something about them. Keeping the status quo is the inverse of that.
I would propose that the case for accidental There isn't an associated benefit to |
(This was originially submitted by @Topsii as a part of #93 / !9489, but I think it's worth its own discussion)
At the moment we have
instance Functor (x,y,)
,instance Bifunctor (x,,)
,instance Bifoldable (x,,)
andinstance Bitraversable (x,,)
, but neitherinstance Foldable (x,y,)
norinstance Traversable (x,y,)
. This is extremely inconsistent. Thus the proposal is to add them, and similar for all tuples up to 7 elements.(7 is not an arbitrary number, Haskell Report suggests that standard functions should be implemented for tuples up to 7 elements)
The text was updated successfully, but these errors were encountered: