-
Notifications
You must be signed in to change notification settings - Fork 16
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
Make ($)
fully representation polymorphic
#132
Comments
Why not |
I thought it would be nicer not to rely on the properties of |
I remember discussion why we don't have |
it might be worth considering that there might be beginners who do not immediately understand how |
Right, I’d really consider this a problem in the UI of haddock, tbh, given that :t doesn’t yield the most general type without flags, but I get your point. |
To be honest, the haddocks for |
@dixonary please ping me when you open a merge request to improve these docs, I'll review it :) |
Right, one may as well say any function is redundant, since you can replace it with its body. |
I very much like |
@dixonary documentation ticket opened at https://gitlab.haskell.org/ghc/ghc/-/issues/22963 |
Would that break something like |
@Vlix It wouldn't break anything (aside from potential type inference issues). The question is how it could affect performance. It won't change performance in your example. |
Seems like a huge footnote. Wrt the actual proposal, can we get the input from @goldfirere since he's been involved in that? I also find there's lack of motivation. The discourse thread links to a theoretical code example. However, that's not really sufficient motivation, IMO. Do users use existing workarounds (such as re-defining Even if it doesn't break anything and has zero performance impact... it will change the type signature of one of the most used functions in Haskell... again. This shouldn't be something we regularly iterate over. |
I run into this issue in a non+theoretical example recently. The definition of -fromParseResult dflags $ parseFile (toFilePath modpath) dflags contents'
+fromParseResult dflags (parseFile (toFilePath modpath) dflags contents') Yes, there's an easy work-around, but I did wonder why the previous definition just not work out of the box.
That's why change to |
@hasufell its not purely theoretic (although I guess I should make a better example), I encountered this issue when I tried to pass a Proxy# instead of a Proxy and use Wrt changing it to Also wrt @phadej's point, consider that we, after all, even have monadic versions of some functions that only need Applicative, I think this falls in the same range - although the simplification would be possible, it might cause confusion so we keep it. |
I yesterday built GHC 9.4.4 with this change, I've yet to try it out on the |
This is not as simple as that.
OTOH, e.g.
Instead of thinking of |
As this was requested by @hasufell, here is the actual motivation for this and the reasoning summarized. I will attach this to the original post. {-# LANGUAGE DataKinds #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wall #-}
module Dollar where
import Data.Kind (Type)
import GHC.Exts (Proxy#, TYPE, proxy#)
import Prelude hiding (($))
-- allows @a@ and @b@ to be representation polymorphic
-- doesn't allow the arrow from @a@ to @b@ to be multiplicity
-- polymorphic
($)
:: forall
repa
repb
(a :: TYPE repa)
(b :: TYPE repb)
. (a -> b)
-> a
-> b
($) f = f
{-# INLINE ($) #-}
infixr 0 $
-- does allow for a function @a -> b@ to be multiplicity and
-- representation polymorphic, but is itself not multiplicity
-- polymorphic until we have something like @id :: forall m a. a %m -> a@
-- this consideration would go away if we'd implement it like
-- @\x -> x@ (same argument for why to use @($) f = f@ instead of
-- @($) = id@)
dollar :: forall (a :: Type). a -> a
dollar = id
{-# INLINE dollar #-}
infixr 0 `dollar`
-- for a motivation on why this is useful and how I encountered
-- this issue with @($)@, consider the following (more contrived,
-- but less complicated than the original) example
type family TF a
class C a where
ca :: Proxy# a -> TF a
-- this would err when using the @($)@ provided by Prelude, although
-- it should literally act as @`id`@
ca' :: forall {proxy} a. C a => proxy a -> TF a
ca' _ = ca $ proxy# @a https://paste.sr.ht/~mangoiv/6fef7284c59202b63de7f26c962ad605b39f7b7f |
evil idea to solve @phadej issues - how about additionally proposing to give |
@MangoIV some people would prefer an application operator that's |
I usually find free-flowing discussions very fruitful, but I know that it can be intimidating / overwhelming for proposers. As a point of order, @MangoIV, you are not obliged to answer every suggestion / generalization / counterproposal.
The latter is not going to change by magic. In the meantime you can backport your changes to
I work a lot with low-level primops and it is very annoying indeed that one cannot use |
@Bodigrim , how much pain would this change alleviate? Wouldn't |
What do you usually do then? Use parentheses or locally define an operator? |
I was too stupid to realise that I can redefine |
I often have to code around this issue also. Sadly most of the Haskell 'base' library doesn't work well with this stuff, so my code is often very custom. I don't think you'll see much code that can use this in the wild until you fix the problems because it makes it very expensive to write in Haskell and end up as a custom parallel micro-ecosystem in my experience. |
@MangoIV how would you like to proceed? Please raise an MR with your preferred version of |
Hi, I'd like to make an impact assessment, but should I build that against ghc 927 or am I blocked until clc stackage has been upgraded to 944 or 961 respectively? If so, I guess I'd volunteer helping to fix it. |
You can use GHC 9.2.7, I would not expect a material difference for this proposal. |
I guess this is the "success" screen:
How do I proceed? |
I'm sympathetic to this change. My understanding is that this change does not make GHC reject code it accepted so far; as such any real world impact should be (assuming all other changes keep the compiler accepting previously accepted code) fairly obvious in alpha/beta/rcs that include this change. I'd still advise to keep an eye out for reported regressions that could be due to this change (or interactions of this change with others). +1 from me. |
Thanks, that's a very interesting observation, a rare case when STG operational semantics becomes visible (https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/compiler/generated-code#dealing-with-generic-application). I agree that in order to be observable this effect requires a combination of factors, most importantly something preventing |
|
Actually, the |
+1 from me I still think that the existing documentation for Discussed in GHC #22963 |
-1 This is an absolutely fundamental Haskell operator and I'm very cautious about the risk we run in changing it without having sufficient experience with it. I think it should live in I also agree with the strong arguments in favour of this change. I think we should eventually do it, if nothing surprising happens when it's used, but I don't think we have sufficient experience with it to take the risk. |
is there some way to write the role polymorphic definition such that it has
the same arity behavior wrt the runtime system? one way to make sure its
unconditionally inlined would be to put the def on the RHS, eg
($) = \ f x -> f x
along with being marked INLINE
what about that?
…On Mon, Mar 27, 2023 at 11:24 AM tomjaguarpaw ***@***.***> wrote:
-1
------------------------------
This is an absolutely fundamental Haskell operator and I'm very cautious
about the risk we run in changing it without having sufficient experience
with it. I think it should live in unlifted-base first, and after a year
or so we can think about making a change to ($) in base too. Packages
that can't depend on base, such as boot packages, can just use a local
definition if they really want it.
I also agree with the strong arguments in favour of this change. I think
we should *eventually* do it, if nothing surprising happens when it's
used, but I don't think we have sufficient experience with it to take the
risk.
—
Reply to this email directly, view it on GitHub
<#132 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAABBQTMXOHUDI7A3B3P6PDW6GWJTANCNFSM6AAAAAAUYZEOLQ>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
@tomjaguarpaw I sympathize, but frankly: the Haskell report demands that both definitions are equivalent. If this causes programs to crash or performance to regress, I'm calling out a bug in GHC. |
@hasufell GHC doesn't implement precise Report Haskell and never has. That's not a bug. |
I'm aware. And I'd still consider it a bug that will have to be fixed in GHC. If we can't swap out |
@hasufell the fact that those are different is actually required by the report. |
Please point me to it. |
@hasufell Polymorphic
In the context of the current, two-argument ($) ⊥ = \y -> ⊥ whereas with a one-argument ($) ⊥ = ⊥ The Report indicates that |
Ah, I thought you're referring to 4.5.5, which I found largely irrelevant here. I think we established that the different strictness properties is something everyone considers impossible to trigger. I was mainly pointing towards:
|
Thanks all, 5 vote in favour out of 7 possible are enough to approve. |
- this change was approved by the CLC in [1] following a CLC proposal [2] - make ($) representation polymorphic (adjust the type signature) - change ($) implementation to allow additional polymorphism - adjust the haddock of ($) to reflect these changes - add additional documentation to document these changes - add changelog entry - adjust tests (move now succeeding tests and adjust stdout of some tests) [1] haskell/core-libraries-committee#132 (comment) [2] haskell/core-libraries-committee#132
Can you create a proposal? |
@andreasabel I believe that's impossible with current GHC. There's no way to avoid a representation polymorphic binding in the |
What do you mean? Isn’t Andreas suggesting that it should have the
analogous type ?
…On Tue, Apr 11, 2023 at 10:58 AM David Feuer ***@***.***> wrote:
@andreasabel <https://github.com/andreasabel> I believe that's impossible
with current GHC. There's no way to avoid a representation polymorphic
binding in the & you seem to suggest.
—
Reply to this email directly, view it on GitHub
<#132 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAABBQRFHS4VVLB6H5RYNVLXAVWR7ANCNFSM6AAAAAAUYZEOLQ>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
Which analogous type? The type being proposed for |
I meant that the type of |
@tomjaguarpaw Are you sure? You voted on MR which has -($) :: forall r a (b :: TYPE r). (a -> b) -> a -> b
+($) :: forall repa repb (a :: TYPE repa) (b :: TYPE repb). (a -> b) -> a -> b We cannot have (&) :: forall repa repb (a :: TYPE repa) (b :: TYPE repb). a -> (a -> b) -> b
x & f = f x that would require representation polymorphic code generation. At best we could have (&) :: forall r a (b :: TYPE r). a -> (a -> b) -> b
x & f = f x i.e. analogous to previous type of |
@hasufell: Opened a proposal at |
Oh whoops, for some reason I thought that the proposal had changed to |
Motivation
At the moment the type of
($)
is:($) :: forall r a (b :: TYPE r). (a -> b) -> a -> b
which means that
a
is not representation polymorphic. It is indeed not possible to make it fully representation polymorphic with the current implementation, which is:f $ x = f x
due to us not knowing what representationx
would be, see this note in the ghc user's guide, however, in the case of($)
this is an implementation detail, i.e. it could be implemented as($) f = f
which would make it unnecessary to pass a representation polymorphic argument.For some more motivation on why this is necessary, please refer to this paste.
Proposal
Like this, we could rewrite the
($)
to:(Indeed, if we could not write
($)
like this, we would even fail to prove (lifted)id
in Haskell, which would be really bad)Alternatives
As pointed out by participants to the conversation, there are multiple alternatives in different directions
Changes to the proposal
($) :: forall a. a -> a
, this will make the function passed to($)
possibly multiplicity polymorphic, this type might however be hard to understand for beginnersAlternatives without the proposal
unlifted-base
which doesn't require the change to actualbase
($)
locally: for this to be convenient, this will require an alternativePrelude
, e.g. with cabalmixins
and some conveniences will fall away e.g. the special rule that makes($)
impredicative everywhere($)
but parens: inconvenientThe text was updated successfully, but these errors were encountered: