-
Notifications
You must be signed in to change notification settings - Fork 269
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
The Eval class proposal #27
Conversation
Nicely written proposal. Why does The main question I have is, when is the user (or the compiler) able to assume η-equivalence? Clearly they can't assume it if they use |
You're right. I guess the only reason I included it in the class is that it was already there in Haskell 1.3 under a different name: class Eval a where
strict :: (a->b) -> a -> b
seq :: a -> b -> b
strict f x = x ‘seq‘ f x
That was my initial assumption, which is why I had included the EtaSafe module property. But I don't think it's necessary to know that the entire module satisfies η-equivalence for any practical purpose. For example, in -- polymorphic seq, not eta-safe, compiled with -XUniversalEval
strictPair :: a -> b -> (a, b)
strictPair !x !y = (x, y)
strictifyList :: Eval a => [a] -> [a]
strictifyList = foldr (uncurry (:) . strictPair) [] it seems to me that I said that the inferred type of |
What are the implementation choices here? What will
be a thunk or a function or a thunk? |
Isnt there also the related question of whether or not seq should have the Would parseq as the semantics simplify understanding this example or On Thu, Nov 10, 2016 at 10:14 AM Joachim Breitner notifications@github.com
|
I'm not an expert on the implementation side, but I expect the The only difference for |
I'm not entirely convinced by this proposal, though I'm more in favor of it after reading than I thought I would be. A few technical points:
To my eyes, you would need the type of
|
You are correct about What do you mean by
We still need to do something about the existing code that uses the standard classes and has explicit type signatures. I don't see how replacing Your type family example blows a big hole in the proposal as it stands; it would only work if GHC could infer the signature of all top-level bindings. I guess reading through the Haskell 1.3 specification took me back to the simpler times when this was still true. All is not lost; the proposal can still fall back on the EtaSafe module alternative. |
Even Haskell98 has functions with an uninferrable type: polymorphic recursive ones. Indeed, my example doesn't require a type family:
This function There's a chance the situation isn't as dire as I'm describing. For example, I believe my You're absolutely right about adding the constraint to standard classes. We would need two versions of everything, which is quite distasteful. I fully retract that claim. |
Some small responses, in haste for now (PLDI is pressing)
I'm frankly dubious about this business about implicitly (silently, invisibly) adding Eval constraints.
I guess
What Eval constraints are implicitly added? Well, it depends on T! For example
We need I think this rabbit hole could get pretty deep. My instinct is to bite the bullet and say that programmers must declare Eval constraints explicitly; and provide both safe and unsafe seq. |
I have found a hoard of 215 email messages in which the Haskell 1.3 committee debated these issues. Lots of interesting things are said, and anyone who is thinking seriously about this proposal should digest them. I'm not keen on publishing them publicly because there are strongly expressed opinions, and it'd be too much work to anonymise; and I don't want to get individual permissions from everyone. But I think it would be kosher for me to send the collection to anyone who asks me individually, and undertakes to use it for the ideas it contains, without quoting who said what. Simon |
Oooh, gossip! I assume there was another long thread when the Haskell 98 committee reverted some of the earlier decisions? Can I have that one as well? |
I guess my naive question is do we need this to be a type class or just something we can interrogate? A related sort of question comes up with exceptions and wanting to ask what exceptions might be thrown by a computation. In both cases this is metadata that could be added to the ghc interface files and we could add info to haddock and commands to ghci for asking "may seq" or may throw. I guess I'm trying to articulate that we may want this information composibly, but not have it interfere with composition? |
You'll have to send me email (simonpj@microsoft.com) so that I get your email address. |
I'm assuming that
where Does "every data type" include functions? I see SPJ already asked this among other questions but without knowing this it's hard to evaluate the proposal. (FWIW, functions were in If it does, then If functions aren't to be an instance of |
# Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
Phil Wadler has argued for unlifted tuples, I managed to dig this up: http://www.mail-archive.com/haskell@haskell.org/msg01466.html (that whole thread is very relevant to this proposal, incidentally). |
Very cool!
Are unlifted tuples distinct from unboxed tuples or should we regard them
as the same?
In this case unlifted tuples could be taken to mean strict tuples that have
a heap pointer representation?
…On Mon, Jan 9, 2017 at 3:55 AM Simon Marlow ***@***.***> wrote:
but I've never heard of a proposal to remove seq for tuples...
Phil Wadler has argued for unlifted tuples, I managed to dig this up:
***@***.***/msg01466.html
(that whole thread is very relevant to this proposal, incidentally).
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#27 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAQwuL2CcISqvh5zjSC082I7s4FM4zgks5rQfYZgaJpZM4KuSDk>
.
|
I'm glad some of these e-mails are still publicly accessible, because it means I won't need to summarize and anonymize I've read through the e-mails simonpj gave me a couple of times. My summary would be that the Eval class got doomed in four steps:
For 1, one of the proposals that had a good run was to allow multiple fields on the newtype constructor, making it an unlifted product. I cannot point to any firm decision not to do this, this proposal and some similar ones seemingly just petered out. Unlifted products would have to be kept out of the http://www.mail-archive.com/haskell@haskell.org/msg01476.html Decisions 2 and 3 have been made in these three messages for the most part: http://www.mail-archive.com/haskell@haskell.org/msg00338.html The Eval class was still kept in Haskell 1.3 for the free parametricity theorems. I'm going to include in full the message that explains this below, because it cannot be find in the mail archives any more. But ultimately it was felt that this purely theoretical benefit of the
|
@blamario, while this proposal is technically at the end of its discussion period, it didn't see much comment from the broader Haskell community. If you would like we can extend the discussion period by a few more weeks to give you an opportunity to solicit more feedback, or we can conclude the comment period and you can opt to bring the proposal to the steering committee for consideration. Which would you rather? |
I will try to solicit some wider feedback, but I should first clarify the proposal a little bit. Give me a day or two. |
I have updated the pull request. For those that have already read it, the only changes were:
|
This proposal has not attracted discussion for a while. Please consider submitting it to the Committee for discussion, if you think it is ready, or close it, if you have abandoned this proposal. |
Very well. The proposal can go before the commitee. |
Correct me if I'm wrong, but if functions are an instance of |
I agree with Reid's points. I'm not sure what this proposal buys me.
I reason about code under the presumption of totality and thus seq is about
when a computation is forced and or ignored.
The example of the eval type class falling over in the presence of
polymorphic recurrence shaped types also is somewhat damning.
I'll reread the proposal but in unclear what sorts of strong reasoning
principles we get out of this style interface
…On Sun, Mar 5, 2017 at 4:30 PM Reid Barton ***@***.***> wrote:
I don't really get what the problem is with polymorphic seq, though. It
breaks parametricity in the mildest possible way. If I have a function f
:: forall a. a -> Int, I don't know that it is constant, but I do know
that the only way it can fail to be constant is by being *|* when its
argument is *|*. More generally any function definable using polymorphic
seq is bounded by a parametric function that is instead defined using notSeq
:: a -> b -> b; notSeq _ y = y. So it can fail to be parametric only by
returning a value that is less defined than expected. We're usually not
that interested in computing _|_s anyways so what's the big deal?
It's a far cry from having a nonparametric operation like a -> Maybe Int,
that is Just for an Int and Nothing for other types (corresponding to
other languages' isinstance), which lets you write new total functions
that you couldn't write without it.
This proposal seems to me like exchanging the well-understood complexity
of polymorphic seq for the poorly-understood complexity of NoUniversalEval;
except that polymorphic seq will still be around, anyways (in "unsafe"
form).
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#27 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAQws9ERGTeeuAyiyvaOHINuAMGR3DSks5riymBgaJpZM4KuSDk>
.
|
I'm also not sure if the issue with bottoms is the main technical barrier with strict / unlifted tuples ... or at least I don't see how newtypes side step the issue. Unboxed tuples, at least as they currently exist, are a representation of register / stack references, and unless they are used to define a field in a data type , they are never on the heap. Or perhaps I'm not understanding how a hypothetical pair as new type would work |
We have to start somewhere. This proposal is the first step that has to be taken if we ever want to take functions out of As for the next step, one thought I had this morning is that provably total functions could be allowed a trivial |
I don't know if they're the main technical barrier either, but bottoms are the main theoretical one. They seem to be the main reason unlifted tuples got rejected during the Haskell 1.3 design. I've already provided the link to the relevant discussion above. Unboxed tuples are not the same thing. Implementation-wise, an unlifted tuple could contain thunks in its fields, but it would never be a thunk itself. I think an unlifted tuple whose fields are all unboxed should be semantically equivalent to an unboxed tuple of today. |
It sounds like you want this:
This is much simpler than existing descriptions. Given the predictably simple shape of
In any case, it sounds like this should be considered under review. If that's the case, please update the label. I would expect that, after this round of revisions, this proposal is ready for final consideration. |
I like the simplicity. :) I don't see how to update the pull request label myself. |
assuming |
But how can |
Maybe we could subclass Eval with something without a function instance to get more bang for buck? |
Definitely, but that would be a follow-on proposal. |
If there can be no such type, |
I appreciate the incrementalism, but I'm worried that unless the deal is sweetened up front, there will be less motivation to make this happen. have you considered making [I suppose the answer to this could relate to how much this class can be erased at runtime.] Granted this isn't a normative part of the RFC, but you mention that unlifted types might not be Eval. I think it's easier and better to simply always consider them in weak head normal form: certainly they'll be represented like head-normalized data of lifted types with GHC (root is boxed, non-closure). This induces a difference between the single field, single constructor unlifted datatype and newtype, but I think that's fine. |
That is actually a workable proposal that could serve as a stepping stone to the present one and further. Another way of putting it is that with The trouble is that the flow of information would then be one way only. The explicit |
I'm still confused about what this proposal gets me as a ghc user. What
code can I write / express that is meaningfully more expressive if I'm
already assuming I don't care about bottoms? Ie : the eta distinction seems
to presume I'm worried about bottoms. I'm not ... either I get a program
result or here's a bug in my code.
Why am I overlooking ?
…On Sat, Mar 11, 2017 at 4:17 PM Mario ***@***.***> wrote:
have you considered making -XUniversalEval more of a hack so as not to
break existing code? Can't we just, as a sledgehammer, discharge all eval
constraints (per-module) with it?
That is actually a workable proposal that could serve as a stepping stone
to the present one and further. Another way of putting it is that with
-XUniversalEval one can insert or import Eval constraints anywhere, and
they'd have no effect whatsoever.
The trouble is that the flow of information would then be one way only.
The explicit Eval constraints imported from modules with
-XNoUniversalEval would go to die in modules with -XUniversalEval. The
present proposal would be able to infer the Eval constraints from the
existing modules. Still, if your idea is easier to implement and eliminates
the backward compatibility problems, it would be a good first step to a
proper Eval class. The second step would be to add {-# LANGUAGE
NoUniversalEval #-} to all base library modules.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#27 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAQwpc7hFR_YqMO_TzhkM92_655Wzdmks5rkw9tgaJpZM4KuSDk>
.
|
Or there's a bug in GHC. Or GHC avoids the bug by conservatively refraining from some useful optimizations. Your argument boils down to Fast and Loose Reasoning is Morally Correct. But that particular result only holds for a subset of Haskell 98. Would you be worried about bottoms in Agda, for example? Haskell appears to be extending in that direction, among others. The lack of a proper theoretical foundation will close off some of those quite practical potential directions. I should make clear that I am not particularly well versed in category nor domain theory. I'm a practical guy, it's just that I'm aware that in practice theory matters. Otherwise I'd probably be programming in PHP or some other thoroughly practical language. |
I'm not sure again precisely what you mean to suggest by this.
You say that this proposal is the first step to a goal, but I'm not sure at all yet that we actually want to get to the goal. |
I think there might be a collision here between different senses of the word "unlifted". I guess the question comes down to whether these unlifted products (or functions, potentially) have kind If If |
There is a good reason for my lack of precision: the statement you quoted is the only thing I'm sure of. In other words, I see the
It would be the latter. |
It took me a minute, but I think I see the problem you bring up :), If OTOH, if the representation for unlifted types coincides with the representation of forced thunks of some type of kind [ |
Right, so this is where the terminology collision comes in I think. In GHC Types of kind However, there is a second meaning of "unlifted" from domain theory. The unlifted product, for example, of two domains is the domain whose underlying set is just the product of the underlying sets of the two domains; while the lifted product (which is what models Haskell's existing
The problem with the first option is that it is incompatible with an implementation
because I'm not sure whether currently have the technology in GHC to make the constraint The second option, of removing unlifted products This still leaves the problem of whether to make functions lifted or unlifted, where just providing both flavors doesn't seem that satisfactory. |
Yeah, I was thinking of this new polymorphism over boxed, unlifted things. My domain theory is pretty weak, but I do think that denotationally, boxed unlifted products are very similar to products that one can only lazily match and never seq. The difference is in programs like On one hand, it's annoying that, with the "No-eval route", it's easier to accumulate chained thunks (whereas one would need to explicitly do such an delaying eta expansion for the "boxed-unlifted route" for that to happen). OTOH, it's annoying to have multiple kinds. |
Okay, but that is a big 'if". Fundamentally the problem is that |
On behalf of the GHC steering committee: This proposal is rejected in its current form. The general consensus was that a language with I will thus close this PR. However, given that the decision hinged more on motivation than a fundamental flaw, do feel free to reopen, revise, and resubmit. Lastly, (on behalf of me, not the committee) I would like to apologize for the long delay in this post. That is no one's fault but my own. Thanks for your patience, and for writing up this proposal. |
This is a short proposal for a new language extension (or perhaps retraction) that tames
seq
by putting it back in theEval
class from Haskell versions 1.3 and 1.4.Rendered proposal