
#12708: RFC: Representation polymorphic Num -------------------------------------+------------------------------------- Reporter: Iceland_jack | Owner: Type: feature request | Status: new Priority: normal | Milestone: Component: Core Libraries | Version: 8.0.1 Resolution: | Keywords: | LevityPolymorphism Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: #12980 | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by goldfire): An exhaustive search through `ghc-prim` and `base` found 34 (!) classes that can be generalized this way: {{{ Eq, Ord, Functor, Applicative, Monad, MonadFail, MonadFix, MonadIO, Bifoldable, Bifunctor, HasResolution, Foldable, Eq1, Ord1, Eq2, Ord2, IsString, TestCoercion, TestEquality, Storable, IsList, Num, Real, Fractional, Generic, Generic1, GHCiSandboxIO, BufferedIO, RawIO, IsLabel, PrintfType, HPrintfType, PrintfArg, IsChar }}} Some are surely more useful to generalize than others. But wow! More classes morally could be included in this list but for two limiting factors: - Many classes use lists somewhere. (For example, `Show`.) However, we could define a family of list types, one for each representation (which would box unboxed tuples), and then use a type family to select the appropriate list type, allowing generalizations of classes that use lists. - Several classes (e.g., `Integral`) use tuples, preventing generalization. But we could just use unboxed tuples and away we go. Another annoyance was that `Applicative` can be generalized to make its parameter `f :: Type -> TYPE r`, but not `f :: TYPE r1 -> TYPE r2`. The latter is impossible because the class definition mentions `f (a -> b)`, and functions are always lifted. It would be lovely, though, to ponder the possibility of `class Applicative (f :: forall (r1 :: RuntimeRep). TYPE r1 -> TYPE r2)`, where `Applicative` would then have a higher-rank kind. And, we could generalize `IO` (it's implemented using an unboxed tuple, after all) and then `IO` could be made an instance of this new `Applicative`. `Monad` would soon follow, and you'd then be able to return unboxed values in `do`-notation. A final note: In testing all this, I had to add `default` signatures imposing a `r ~ LiftedRep` constraint whenever a generalized class had a default implementation, because the default implementation would work only at kind `Type`. This works, but it's disappointing that there can be no defaults at representations other than `LiftedRep`. However, I've realized we ''can''. Instead of `r ~ LiftedRep` in the `default` signature, impose a `Typeable r` constraint. Then we can do a runtime type test to figure out the representation and to squash the levity polymorphism. (This actually works.) A sufficiently smart compiler would be able to optimize away the runtime type test in any non-levity-polymorphic class instance. And we're just left with happiness and a wicked powerful type system. My (rushed, somewhat shoddy) work on this is [https://github.com/goldfirere/ghc/tree/lev-poly-base here]. I don't expect that to ever be merged -- I just was exploring the possibility. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/12708#comment:29 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler