haskell / rfcs

This repo is archived, consider using https://github.com/ghc-proposals/ghc-proposals instead
98 stars 17 forks source link

Add empty data proposal #5

Open quchen opened 8 years ago

quchen commented 8 years ago

Rendered version: https://github.com/quchen/rfcs/blob/empty-data/texts/0000-empty-data.rst

quchen commented 8 years ago

Interesting, @hvr. But the Report does not specify what the extension does, is that correct? This would degenerate this proposal to a to-do marker to add this properly to the spec.

goldfirere commented 8 years ago

Haskell 2010 includes empty data declarations (what's missing in the spec? They're just data declarations with no constructors) but not empty case. So I believe this degenerates to an inclusion of empty case. Do you agree?

goldfirere commented 8 years ago

We also must specify that the empty-case must always be strict in the scrutinee. Otherwise,

let v :: Void
    v = error "blargh"
in case v of {}

will yield an incomplete pattern-match exception instead of blargh, as it should.

quchen commented 8 years ago
case v :: Void of {}

will yield an incomplete pattern-match exception

Although it would not be incorrect to throw an incomplete pattern exception here, I’m assuming what you want is the “right” exception?

goldfirere commented 8 years ago

Yes, that's right. Evaluating the scrutinee gives the more informative exception, in my opinion... because the pattern-match is actually complete. This is what GHC already does today; I just wanted to clarify the proposal.

quchen commented 8 years ago

So is it correct that the following rule would have the desired properties, @goldfirere? What should we do if v is not nullary?

Section 3.17.3, Formal semantics of pattern matching

case v of {}
  = v

where v is a value of an uninhabited type

goldfirere commented 8 years ago

Not quite. I propose

Section 3.17.3, Formal semantics of pattern matching

case v of {}
  = v `seq` error "No match"

I believe that covers all cases.

While we're here, I noticed a bug in the report: The first sentence of 3.17.2 is "Patterns are matched against values." But this is false! case undefined of _ -> () evaluates to (), matching the pattern _ against the non-value undefined. This is handled correctly in the formal semantics.

quchen commented 8 years ago

@goldfirere That translation isn’t correct yet, since the compiler may arbitrarily choose which argument of seq to force first, thus yielding the less helpful “No match” error in all cases. More info here.

cartazio commented 8 years ago

I recall some remarks or discussion somewhere that seq should perhaps have the semantics of pseq, or something to that effect. Is that relevant for this ?

On Friday, August 26, 2016, David Luposchainsky notifications@github.com wrote:

@goldfirere https://github.com/goldfirere That translation isn’t correct yet, since the compiler may arbitrarily choose which argument of seq to force first. More info here. https://github.com/quchen/articles/blob/master/fbut.md#seq-does-not-specify-an-evaluation-order

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/haskell/rfcs/pull/5#issuecomment-242753890, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAQwoVQaZo7Z0aRUV1MujS_m4DGPpcaks5qjvqkgaJpZM4JgPgh .

goldfirere commented 8 years ago

@quchen: Good point. :(

quchen commented 8 years ago

@goldfirere I think we only need to handle the other case, namely when the value is inhabited, as well. New rule suggestion:

case v of {} = v -- if v is an empty type
             = error "No match" -- otherwise
goldfirere commented 8 years ago

How do we know when a type is empty? For example:

data C where
   MkC :: 1 `NotIn` CollatzSequence n => Proxy n -> C

No human (to my knowledge) knows whether or not C is empty. We can't have our semantics depend on type inhabitation.

On the other hand, we could just say

case v of {}
  = v `pseq` error "No match"

That works. But it means we now need to specify pseq.

cartazio commented 8 years ago

Hrm, I think José and one or two other folks were in favor is formalizing pseq (though I guess I'm repeating myself)

Is the semantic difference of seq vs pseq articulated anywhere? When would code benefit from seq rather than pseq? My understanding is that seq provides a weaker order of reduction to whnf guarantee vs pseq, whereas pseq guarantees that the first Argument will be reduced to whnf BEFORE the result argument

On Tuesday, August 30, 2016, Richard Eisenberg notifications@github.com wrote:

How do we know when a type is empty? For example:

data C where MkC :: 1 NotIn CollatzSequence n => Proxy n -> C

No human (to my knowledge) knows whether or not C is empty. We can't have our semantics depend on type inhabitation.

On the other hand, we could just say

case v of {} = v pseq error "No match"

That works. But it means we now need to specify pseq.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/haskell/rfcs/pull/5#issuecomment-243532636, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAQwu3Hn4g_aBew-60edlr95LC1tnozks5qlHTngaJpZM4JgPgh .

augustss commented 8 years ago

I really don't like the idea of case evaluating the scrutinee. It's totally irregular compared to how case is explained now.

On Tuesday, August 30, 2016, Richard Eisenberg notifications@github.com wrote:

How do we know when a type is empty? For example:

data C where MkC :: 1 NotIn CollatzSequence n => Proxy n -> C

No human (to my knowledge) knows whether or not C is empty. We can't have our semantics depend on type inhabitation.

On the other hand, we could just say

case v of {} = v pseq error "No match"

That works. But it means we now need to specify pseq.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/haskell/rfcs/pull/5#issuecomment-243532636, or mute the thread https://github.com/notifications/unsubscribe-auth/AFAInv5Onax_xRu8cmgZrnuybKBe_tikks5qlHTogaJpZM4JgPgh .

goldfirere commented 8 years ago

I don't like it from a theory standpoint, but I also don't like case (error "urk" :: Void) of {} throwing an incomplete-pattern-match exception. The pattern match most certainly is complete! Evaluating the scrutinee seems better from a user standpoint, even if I agree that it is a design wart.

augustss commented 8 years ago

I don't think we need to specify exactly what error we will get I that situation. Just as (error "a" + error "b") is non-deterministic, we can leave case on an empty type non-deterministic.

On Friday, September 2, 2016, Richard Eisenberg notifications@github.com wrote:

I don't like it from a theory standpoint, but I also don't like case (error "urk" :: Void) of {} throwing an incomplete-pattern-match exception. The pattern match most certainly is complete! Evaluating the scrutinee seems better from a user standpoint, even if I agree that it is a design wart.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/haskell/rfcs/pull/5#issuecomment-244396329, or mute the thread https://github.com/notifications/unsubscribe-auth/AFAInrv-2rNXj6kuy0r1nyv-NAbwUe32ks5qmDdcgaJpZM4JgPgh .

goldfirere commented 8 years ago

Isn't your suggestion precisely

case v of {}
  = v `seq` error "No match"

? Due to the lack of tight specification for seq, this should do what you just suggested. I feel I'm missing something here, though.

quchen commented 8 years ago

Good point, @goldfirere. Using seq might give implementors actually more flexibility here!

augustss commented 8 years ago

Excellent point.

On Friday, September 2, 2016, David Luposchainsky notifications@github.com wrote:

Good point, @goldfirere https://github.com/goldfirere. Using seq might give implementors actually more flexibility here!

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/haskell/rfcs/pull/5#issuecomment-244478122, or mute the thread https://github.com/notifications/unsubscribe-auth/AFAIntI5L0bmwnSc8d1djgci3zkV6GC0ks5qmIUYgaJpZM4JgPgh .

wrengr commented 8 years ago

@cartazio I believe it's articulated in the paper where pseq was first introduced. AFAICR: seq ensures that the left argument is forced some time before the whole seq expression returns a value (but this may be before, after, or interleaved with evaluation of the right argument), whereas pseq ensures that the left argument is forced (and finishes) before starting evaluation of the right argument.

The advantage of the weaker requirements on seq is, of course, that it gives compiler-writers more wiggle room and so could allow for better performance.

quchen commented 8 years ago

@wrengr seq does not even guarantee that: if seq x y finds out that y is bottom, then the result is bottom, regardless of x. The only way we can force a (sufficiently strange) compiler to check the x is by showing it that y is not bottom.

yav commented 8 years ago

What use case are we trying to support by adding the implicit seq to the translation of empty case?

goldfirere commented 8 years ago

Without the seq, then case (error "I have type Void" :: Void) of {} would reduce to error "Incomplete match", even thought the match is actually complete! It's the construction of a term of type Void that's the problem.

yav commented 8 years ago

Well, the error does not have to say "Incomplete match", it could be "Evaluated an empty case". I see that it might be preferable to pick the user error though. Hm.