[GHC] #14917: Allow levity polymorphism in binding position
GHC
ghc-devs at haskell.org
Sun Apr 8 20:26:06 UTC 2018
#14917: Allow levity polymorphism in binding position
-------------------------------------+-------------------------------------
Reporter: andrewthad | Owner: (none)
Type: feature request | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 8.2.2
Resolution: | Keywords:
| LevityPolymorphism
Operating System: Unknown/Multiple | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by josef):
I've thought about this problem for some time, but never written down
my ideas before. Now seemed like a good time to speak up though :-) Below
are some hopefully coherenet notes.
I'd introduce a new quantifier (let's call it "V") which provides
'''template polymorphism'''. This quantifier can quantify over all kinds,
even levity polymorphic things. The idea is that whenever a function
of template polymorphic type is called, a new copy of the function
will be created, specialized with the instantiated type for its
template polymorphic type variables.
Example:
{{{
id :: V r (a :: TYPE r). a -> a
id a = a
}}}
When we use it like in an expression like this `id #5` or `id "foo"`
the typechecker creates new bindings `id_#_Int#` and `id_*_String`. These
new bindings are then used as part of elaborating the expressions:
`id_#_Int# #5` and `id_*_String "foo"`.
{{{
id_#_Int# :: Int# -> Int#
id_#_Int# a = a
id_*_String :: String -> String
id_*_String a = a
}}}
More formally: A call to a template polymorphic function must
instantiate all template polymorphic type parameters. These parameters
must be instantiated with either fully ground types without any
variables, or variables bound by "V", the template polymorphic
quantifier.
A trivial point, that I still want to point out: given two (or more)
calls to the same template polymorphic function with the same type
instantiation only generates one new specialization.
Most likely a more sophisticated naming scheme than the one I've used
here will be needed to ensure that the names of the new bindings are
unique.
The quantifier is only allowed to be used on the top-level and must
appear before any usual quantifiers. I.e. template polymorphism is rank-1.
Quantified template polymorphism, i.e. having class constraints on
template polymorphic type variables is not a problem.
== Recursion ==
It is perfectly possible to support monomorphic recursion for template
polymorphism. In fact, the elaboration outlined above supports
monomorphic recursion out of the box. The recursive call inside of
template polymorphic function would generate the same specialization
as a call from outside the function, and since we don't generate
multiple specializations, the call will simply be specialized to a
recursive call in the new specialized function.
It is likely possible to support special cases of polymorphic
recursion, but it is not clear to me that it is worth the extra work.
== Modules ==
When exporting a template polymorphic function, it's specializations
should also be exported, to avoid re-generating them. However, the
importing module will have to creating its own new specializations if
needed.
This can lead to a situation where a module has two imports which
exports the same specialization of a function they in turn
imported. But there is no real problem here, because it doesn't matter
which one we pick since both specializations will be the same.
== Extensions ==
One of the interesting things about this proposal is that generalizes
nicely
to data types.
Example:
{{{
data List (V r) (a :: TYPE r) = Cons a (List r a) | Nil
}}}
This way we get a list data type which can be used at any kind. After
type checking and elaboration we will have one list data type for
each runtime representation.
However, supporting specialization in different modules might require a
bit of thought.
== Final thoughts ==
The gist of this proposal is that it gives control over optimization
to the library writer, similarly to the RULES pragma. Though it will not
help any existing programs go faster.
I'd really like to see something like this in GHC. Unfortunately, I don't
have the cycles to flesh out this proposal and implement it. But I'd be
happy to advice if someone wants to have a go at it.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14917#comment:15>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list