Closed EliasC closed 7 years ago
Bravo! I want this! I'll read in detail and comment.
However, as long as we have null in the language, not all values can be consumed in a way that makes sense (what is null for an int, for example).
What does it mean to consume an int
?
What about considering bounded type polymorphism? Would that encompass bounded mode polymorphism?
What does it mean to consume an
int
?
Well, consuming an int
doesn't mean anything, but consuming a variable means promising not to touch it for the remainder of the scope, which makes sense for variables storing int
s as well. From a data-race prevention point of view, you never need to treat a primitive linearly, but for polymorphism purposes, you might do so only to be able to use the same function for both linear stuff and primitive stuff.
What about considering bounded type polymorphism? Would that encompass bounded mode polymorphism?
Definitely related, and something that we should be adding! It is however a bit different from mode polymorphism as it typically constrains the interface of the type rather than the mode. With an Any
type I guess you could achieve the same thing by writing def foo[shared t]()
as def foo[t : shared Any]()
, assuming that primitives are also subtypes of Any
. When we go for implementing bounded type polymorphism, we could consider this as desugaring.
Is there something more than consuming access to the variable? For example, are you worried about the reference hanging around for the GC to misinterpret? I guess with fields it is hard to remove the "access the field" capability. Is that why you need some notion of "null"?
Is there something more than consuming access to the variable? For example, are you worried about the reference hanging around for the GC to misinterpret?
Not really, it's just a matter of ensuring that whoever is accessing the underlying value has exclusive access. It's fine if the pointer is still on the stack, as long as it's not accessed.
I guess with fields it is hard to remove the "access the field" capability. Is that why you need some notion of "null"?
That's right. The current implementation allows consuming Maybe values (leaving Nothing
behind), and when we get rid of null
this will be the only way to get something from a field of linear type.
Closing this since #776 was merged!
Problem
Parametric polymorphism is useful for writing general code because it abstracts over the type of arguments and fields:
In Kappa however, the type of a value sometimes incurs restrictions on how that value may be used, for example by precluding sharing (
local
) or aliasing (linear
). By abstracting "too far", we may lose information about these restrictions and possibly break them:Current approach
In the open Kappa PR, we restrict polymorphic values in two ways. First, we disallow passing polymorphic values to other actors. For the
leak
function above, the error isSecondly, we disallow using
linear
(andsubord
) values as type arguments. When calling theduplicate
function above, the error isThe type arguments that are allowed in practice are primitive types, and capabilities with the mode
read
,local
oractive
. This will most likely cover most use-cases of polymorphism, but since we are developing a language for concurrency and parallelism, it would be a shame to disallow thread communication in polymorphic contexts.More general solution: bounded mode polymorphism
In a feature branch based on Kappa, I have implemented an extension to parametric polymorphism which allows specifying bounds on the mode of a type argument. To abstract over the types whose values are safe to share across active objects (primitives,
read
andactive
), there is a new abstract modeshared
. For example, we can safely make the theleak
function compile by annotating its type parameter asshared
:When trying to call
leak
with alocal
value, the type error isIf we wanted
leak
to work forlinear
types as well, we would annotate it aslinear
, requiring us toconsume
x
when we send it to theThief
. Notably, whenleak
has a type parameter marked aslinear
, it is safe to call with non-linear values as well; treating an already aliased value linearly does not break any invariants. However, as long as we havenull
in the language, not all values can be consumed in a way that makes sense (what isnull
for anint
, for example). Therefore, we require the value being consumed to be wrapped in aMaybe
type (whose consuming sets the variable toNothing
):If no
consume
is used, there is no need to useMaybe
. Also, once there is a proper flow sensitive analysis that statically disallows duplicate uses of linear values, this restriction can also be dropped.When no mode is specified, the semantics are as described in the previous section.