Retro-Haskell: can we get seq somewhat under control?

In the Old Days (some time before Haskell 98), `seq` wasn't fully polymorphic. It could only be applied to instances of a certain class. I don't know the name that class had, but let's say Seq. Apparently, some people didn't like that, and now it's gone. I'd love to be able to turn on a language extension, use an alternate Prelude, and get it back. I'm not ready to put up a full-scale proposal yet; I'm hoping some people may have suggestions for details. Some thoughts: 1. Why do you want that crazy thing, David? When implementing general-purpose lazy data structures, a *lot* of things need to be done strictly for efficiency. Often, the easiest way to do this is using either bang patterns or strict data constructors. Care is necessary to only ever force pieces of the data structure, and not the polymorphic data a user has stored in it. 2. Why does it need GHC support? It would certainly be possible to write alternative versions of `seq`, `$!`, and `evaluate` to use a user-supplied Seq class. It should even be possible to deal with strict data constructors by hand or (probably) using Template Haskell. For instance, data Foo a = Foo !Int !a would translate to normal GHC Haskell as data Foo a = Seq a => Foo !Int !a But only GHC can extend this to bang patterns, deal with the interactions with coercions, and optimize it thoroughly. 3. How does Seq interact with coercions and roles? I believe we'd probably want a special rule that (Seq a, Coercible a b) => Seq b Thanks to this rule, a Seq constraint on a type variable shouldn't prevent it from having a representational role. The downside of this rule is that if something *can* be forced, but we don't *want* it to be, then we have to hide it a little more carefully than we might like. This shouldn't be too hard, however, using a newtype defined in a separate module that exports a pattern synonym instead of a constructor, to hide the coercibility. 4. Optimize? What? Nobody wants Seq constraints blocking up specialization. Today, a function foo :: (Seq a, Foldable f) => f a -> () won't specialize to the Foldable instance if the Seq instance is unknown. This is lousy. Furthermore, all Seq instances are the same. The RTS doesn't actually need a dictionary to force something to WHNF. The situation is somewhat similar to that of Coercible, *but more so*. Coercible sometimes needs to pass evidence at runtime to maintain type safety. But Seq carries no type safety hazard whatsoever--when compiling in "production mode", we can just *assume* that Seq evidence is valid, and erase it immediately after type checking; the worst thing that could possibly happen is that someone will force a function and get weird semantics. Further, we should *unconditionally* erase Seq evidence from datatypes; this is necessary to maintain compatibility with the usual data representations. I don't know if this unconditional erasure could cause "laziness safety" issues, but the system would be essentially unusable without it. 4. What would the language extension do, exactly? a. Automatically satisfy Seq for data types and families. b. Propagate Seq constraints using the usual rules and the special Coercible rule. c. Modify the translation of strict fields to add Seq constraints as required. David Feuer