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
Split RecordDotSyntax into two extensions #405
Split RecordDotSyntax into two extensions #405
Conversation
While it is not hard to deduce, I think proposal should be clear of the effects when only one extension is enabled. Also if the only motivation is to get one part into 9.2 and the second later (into 9.4). Can't we wait with both for 9.4? |
Actually we're angling to get both parts in 9.2 however, the |
582233e
to
f4f27fe
Compare
Thanks for the feedback. I added this detail to section 2.1. |
Just curious the proposal says
Would this possible extension fall under Rephrasing it to
does not really make it easier to understand at first glance. Before the split into two extensions this complexity was rather elegantly hidden. Will there still be a |
I think this change is independently useful, even once both are implemented, because it means users have the option to switch on one feature without the other. There are enough changes coming regarding records (e.g. This may be particularly helpful when porting an existing codebase, because both extensions are non-conservative in different ways:
It's not yet certain that |
I think neither, because it isn't overloaded. The data constructor determines which type is meant. So it would really be a small syntactic extension to record pattern-matching syntax. But in any case that would require a separate proposal. |
@adamgundry so the @shayne-fletcher comment
is a white lie? |
@phadej I'd rather say it is a plausible prediction, just not a guarantee. 😉 I'm fairly optimistic we can hit 9.4, but it depends on whether my upcoming proposal gets mired in debate and whether the implementation hits any more blockers. |
45926fd
to
ed06974
Compare
Then I'll ask for At the very least the intermediate state should be brought to the committee for discussion. |
48db6e8
to
d2ca25d
Compare
I wonder about the names of the extensions. I think
It's future might change based on future proposals. But as it stands currently, it's based on accepted but not yet fully implemented proposals. However, the I note that |
I think it would be helpful to summarise the changes embodied by this PR. |
|
You can view the rendered version here. |
I understood the motivation for In any case, regarding specific names, let me know and I'll adjust... I'm easy! |
I don't feel terribly strongly about naming, but there is precedent for
I wondered about this. I do generally prefer the approach of defining small orthogonal extensions first, then specifying larger features as the conjunction of such extensions. Moreover my feeling is that |
As a user, the presence of many fine grained possibilities doesn't always help me, because I'm not paged in to the nuanced trade-offs.
This, however, makes sense. As long as we end up at a satisfying whole, I agree that it's good to build up pieces incrementally. So I hope this proposal is seen as an enabler for RecordDotSyntax rather than a replacement. |
d2ca25d
to
32b8f92
Compare
If |
Yes, my understanding is that when |
@nomeata I'd like to formally ask to turn consideration of this amendment over to the committee now please. |
{-# LANGUAGE OverloadedRecordDot, DuplicateRecordFields #-}
data T = T { x :: Int }
data S = S { x :: Int }
f a = a { x = 2 }
It requires disambiguation
|
Your example does not involve any dots, so it'd be unaffected by this proposal, no? |
To expand a bit on the motivation, a couple of concrete cases where the split would be useful:
These are meaningfully distinct features, even though they were originally proposed together as one |
OK, I am confused about the myriad of record extensions:
On the topic of splitting the two, I agree it would be useful to do so, but I'd like to raise the question if we should have |
You are correct.
Great!
Sorry, this was debated when the original proposal was considered and accepted. I don't think it falls into the category of things that are being considered with respect to this amendment. [BTW @yav I draw your attention to 7.6 Should a new update syntax be added?] |
@shayne-fletcher can you point me to something that outlines what's the long term plan for the language? Which of these futures are we aiming for:
Perhaps there is another option, I'd be interested to hear. There was a lot of pressure to accept the original proposal, and most of the discussion was about the syntactic issues to do with |
Sadly I myself simply can't do this for you @yav , I must defer to others. I do think you have posed very good questions and I too look forward to hearing what can be said in response to them! |
This point was discussed at length. But GitHub doesn't support the length the conversation grew to, so it's probably infeasible to point you at where. There is reference to it as a drawback in https://ghc-proposals.readthedocs.io/en/latest/proposals/0282-record-dot-syntax.html#id13.
Restricting record updates is useful to get decent type checking. Otherwise, given unconstrained types, expressions like We could certainly come up with a new syntax for monomorphic vs polymorphic desugaring. And furthermore, I recommend As to whether there is a way to have polymorphic update, but enough type constraints to make it have decent inference, that's connected to this proposal, but slightly different. If you switch out setField with something that allows type changing update, it would work. Given the RebindableSyntax plus this proposal, we'd have the freedom to experiment. |
If a typeclass like
It could be used like this:
Annoyingly, it seems to require |
@yav asks which of these we are aiming for
That particular question is a reasonable one, but it could be asked of many other extensions, including About overloaded, non-type-changing record update
|
proposals/0282-record-dot-syntax.rst
Outdated
7.7 Why two extensions and not just one? | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
Having two extensions provides fine grained control. For example, one | ||
can enable the use of ``.`` for field selection without affecting | ||
record updates. Conversely, one can enable overloaded record updates | ||
without giving ``.`` new meaning. |
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.
This looks like a pretty weak motivation to have two extensions. After all, extensions have a cost, so we must get a real benefit for that cost.
That being said, my understanding of the thread is that there are, indeed, stronger motivations. At least the fact that the record update syntax is not backward compatible should be brought up. I'm not entirely sure I managed to understand what motivated everyone involved. But it's alright: that's what this section is about, it should document the motivations which have accumulated through the threads (and objections as well). That would be really helpful!
Thanks Arnaud. I think the alternatives and their motivations are pretty straightforward:
There is nothing deep here. I don't think it matters much which we choose, but we should choose. My own recommendation is (1), but if there is a consensus for (2) or (3), or someone wants to suggest another alternative, that would be equally fine. |
There is no need to come up with a different syntax, type-changing updates with good type inference are possible.
Below snippet demonstrates that you can have type-changing updates compatible with overloaded fields with the same type-inference properties as the classic record update syntax. type-changing.hs{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NamedWildCards #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE TypeApplications #-}
module TypeChanging where
import GHC.Generics (Generic)
import Optics.Core
data Pet = Pet
{ name :: String
} deriving (Show, Generic)
data User ph a = User
{ name :: String
, age :: a
, pet :: Maybe Pet
} deriving (Show, Generic)
-- signature inferrable by ghc, can be written down
general :: ( Num b1
, GField "name" s3 s1 a2 [a3]
, GField "age" s1 s2 a1 b1
, GField "pet" s2 b2 a4 (Maybe a5)
) => s3 -> b2
general user = user & gfield @"name" .~ []
& gfield @"age" .~ 23
& gfield @"pet" .~ Nothing
-- ../type-changing.hs:33:22: warning: [-Wpartial-type-signatures]
-- • Found type wildcard ‘_ph’ standing for ‘a’
-- Where: ‘a’ is a rigid type variable bound by
-- the inferred type of infer_output :: User a a1 -> User a2 Integer
-- at ../type-changing.hs:34:1-22
-- • In the type signature: infer_output :: User _ph _a -> _output
-- |
-- 33 | infer_output :: User _ph _a -> _output
-- | ^^^
--
-- ../type-changing.hs:33:26: warning: [-Wpartial-type-signatures]
-- • Found type wildcard ‘_a’ standing for ‘a1’
-- Where: ‘a1’ is a rigid type variable bound by
-- the inferred type of infer_output :: User a a1 -> User a2 Integer
-- at ../type-changing.hs:34:1-22
-- • In the type signature: infer_output :: User _ph _a -> _output
-- |
-- 33 | infer_output :: User _ph _a -> _output
-- | ^^
--
-- ../type-changing.hs:33:32: warning: [-Wpartial-type-signatures]
-- • Found type wildcard ‘_output’ standing for ‘User a2 Integer’
-- Where: ‘a2’ is a rigid type variable bound by
-- the inferred type of infer_output :: User a a1 -> User a2 Integer
-- at ../type-changing.hs:34:1-22
-- • In the type signature: infer_output :: User _ph _a -> _output
-- |
-- 33 | infer_output :: User _ph _a -> _output
infer_output :: User _ph _a -> _output
infer_output = general
-- ../type-changing.hs:36:16: warning: [-Wpartial-type-signatures]
-- • Found type wildcard ‘_input’ standing for ‘User a a1’
-- Where: ‘a’, ‘a1’ are rigid type variables bound by
-- the inferred type of infer_input :: User a a1 -> User a2 Integer
-- at ../type-changing.hs:37:1-21
-- • In the type signature: infer_input :: _input -> User _ph _a
-- |
-- 36 | infer_input :: _input -> User _ph _a
-- | ^^^^^^
--
-- ../type-changing.hs:36:31: warning: [-Wpartial-type-signatures]
-- • Found type wildcard ‘_ph’ standing for ‘a2’
-- Where: ‘a2’ is a rigid type variable bound by
-- the inferred type of infer_input :: User a a1 -> User a2 Integer
-- at ../type-changing.hs:37:1-21
-- • In the type signature: infer_input :: _input -> User _ph _a
-- |
-- 36 | infer_input :: _input -> User _ph _a
-- | ^^^
--
-- ../type-changing.hs:36:35: warning: [-Wpartial-type-signatures]
-- • Found type wildcard ‘_a’ standing for ‘Integer’
-- • In the type signature: infer_input :: _input -> User _ph _a
-- |
-- 36 | infer_input :: _input -> User _ph _a
infer_input :: _input -> User _ph _a
infer_input = general |
39fae1e
to
59d5cfe
Compare
59d5cfe
to
15c40bb
Compare
Just to close the loop on this proposal:
Please merge the change into the original proposal. I understand that the implementation will be out in GHC 9.2. Thank you, Shayne, Neil, Adam, for doing this work! |
@shayne-fletcher , can I press the merge button, or is there still something to be done here? |
Thanks @nomeata , this is good to merge. |
The functionality originally to be provided by the single language extension
RecordDotSyntax
is better provided by two orthogonal but related extensionsOverloadedRecordSelection
andOverloadedRecordUpdate
.See !4532(comment 330639) for context.