Deriving functions from methods

DELTA=238  (118 added, 116 deleted, 4 changed)
OCL=34653
CL=34660
This commit is contained in:
Rob Pike 2009-09-15 15:56:44 -07:00
parent 1c9e4b358f
commit 01cadde597

View File

@ -1750,7 +1750,7 @@ and a type.
Operands denote the elementary values in an expression. Operands denote the elementary values in an expression.
<pre class="ebnf"> <pre class="ebnf">
Operand = Literal | QualifiedIdent | "(" Expression ")" . Operand = Literal | QualifiedIdent | MethodExpr | "(" Expression ")" .
Literal = BasicLit | CompositeLit | FunctionLit . Literal = BasicLit | CompositeLit | FunctionLit .
BasicLit = int_lit | float_lit | char_lit | StringLit . BasicLit = int_lit | float_lit | char_lit | StringLit .
</pre> </pre>
@ -2710,122 +2710,6 @@ to by the operand.
*pf(x) *pf(x)
</pre> </pre>
<p>
<font color=red>TODO: This text needs to be cleaned up and go elsewhere, there are no address
operators involved.
</font>
</p>
<p>
Methods are a form of function and a method ``value'' has a function type.
Consider the type T with method M:
</p>
<pre>
type T struct {
a int;
}
func (tp *T) M(a int) int;
var t *T;
</pre>
<p>
To construct the value of method M, one writes
</p>
<pre>
t.M
</pre>
<p>
using the variable t (not the type T).
<font color=red>TODO: It makes perfect sense to be able to say T.M (in fact, it makes more
sense then t.M, since only the type T is needed to find the method M, i.e.,
its address). TBD.
</font>
</p>
<p>
The expression t.M is a function value with type
</p>
<pre>
func (t *T, a int) int
</pre>
<p>
and may be invoked only as a function, not as a method:
</p>
<pre>
var f func (t *T, a int) int;
f = t.M;
x := f(t, 7);
</pre>
<p>
Note that one does not write t.f(7); taking the value of a method demotes
it to a function.
</p>
<p>
In general, given type T with method M and variable t of type T,
the method invocation
</p>
<pre>
t.M(args)
</pre>
<p>
is equivalent to the function call
</p>
<pre>
(t.M)(t, args)
</pre>
<p>
<font color=red>
TODO: should probably describe the effect of (t.m) under §<a href="#Expressions_if_t">Expressions if t</a>.m
denotes a method: Effect is as described above, converts into function.
</font>
</p>
<p>
If T is an interface type, the expression t.M does not determine which
underlying type's M is called until the point of the call itself. Thus given
T1 and T2, both implementing interface I with method M, the sequence
</p>
<pre>
var t1 *T1;
var t2 *T2;
var i I = t1;
m := i.M;
m(t2, 7);
</pre>
<p>
will invoke t2.M() even though m was constructed with an expression involving
t1. Effectively, the value of m is a function literal
</p>
<pre>
func (recv I, a int) {
recv.M(a);
}
</pre>
<p>
that is automatically created.
</p>
<p>
<font color=red>
TODO: Document implementation restriction: It is illegal to take the address
of a result parameter (e.g.: func f() (x int, p *int) { return 2, &amp;x }).
(TBD: is it an implementation restriction or fact?)
</font>
</p>
<h3 id="Communication_operators">Communication operators</h3> <h3 id="Communication_operators">Communication operators</h3>
<p> <p>
@ -2915,10 +2799,128 @@ zero value for its type (§<a href="#The_zero_value">The zero value</a>).
</p> </p>
<p> <p>
<font color=red>TODO: Probably in a separate section, communication semantices <font color=red>TODO: Probably in a separate section, communication semantics
need to be presented regarding send, receive, select, and goroutines.</font> need to be presented regarding send, receive, select, and goroutines.</font>
</p> </p>
<h3 id="Method_expressions">Method expressions</h3>
<p>
If <code>M</code> is in the method set of type <code>T</code>,
<code>T.M</code> is a function that is callable as a regular function
with the same arguments as <code>M</code> prefixed by an additional
argument that is the receiver of the method.
</p>
<pre class="grammar">
MethodExpr = ReceiverType "." MethodName .
ReceiverType = TypeName | "(" "*" TypeName ")" .
MethodName = identifier .
</pre>
<p>
Consider a struct type <code>T</code> with two methods,
<code>Mv</code>, whose receiver is of type <code>T</code>, and
<code>Mp</code>, whose receiver is of type <code>*T</code>.
</p>
<pre>
type T struct {
a int;
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float) float { return 1 } // pointer receiver
var t T;
</pre>
<p>
The expression
</p>
<pre>
T.Mv
</pre>
<p>
yields a function equivalent to <code>Mv</code> but
with an explicit receiver as its first argument; it has signature
</p>
<pre>
func (tv T, a int) int
</pre>
<p>
That function may be called normally with an explicit receiver, so
these three invocations are equivalent:
</p>
<pre>
t.Mv(7)
T.Mv(t, 7)
f := T.Mv; f(t, 7)
</pre>
<p>
Similarly, the expression
</p>
<pre>
(*T).Mp
</pre>
<p>
yields a function value representing <code>Mp</code> with signature
</p>
<pre>
func (tp *T, f float) float
</pre>
<p>
For a method with a value receiver, one can derive a function
with an explicit pointer receiver, so
</p>
<pre>
(*T).Mv
</pre>
<p>
yields a function value representing <code>Mv</code> with signature
</p>
<pre>
func (tv *T, f int) int
</pre>
<p>
Such a function indirects through the receiver to create a value
to pass as the receiver to the underlying method;
the method does not overwrite the value whose address is passed in
the function call.
</p>
<p>
The final case, a value-receiver function for a pointer-receiver method,
is illegal because pointer-receiver methods are not in the method set
of the value type.
</p>
<p>
Function values derived from methods are called with function call syntax;
the receiver is provided as the first argument to the call.
That is, given <code>f := T.Mv</code>, <code>f</code> is invoked
as <code>f(t, 7)</code> not <code>t.f(7)</code>.
To construct a function that binds the receiver, use a
<a href="Function_literals">closure</a>.
</p>
<p>
It is legal to derive a function value from a method of an interface type.
The resulting function takes an explicit receiver of that interface type.
</p>
<h3 id="Constant_expressions">Constant expressions</h3> <h3 id="Constant_expressions">Constant expressions</h3>
<p> <p>
@ -4309,8 +4311,8 @@ mentions <code>B</code>, or mentions a function that
mentions <code>B</code>, recursively. mentions <code>B</code>, recursively.
If two items are not interdependent, they will be initialized If two items are not interdependent, they will be initialized
in the order they appear in the source. in the order they appear in the source.
Since the dependency analysis is done per package, it can be Since the dependency analysis is done per package, it can produce
defeated if <code>A</code>'s initializer calls a function defined unspecified results if <code>A</code>'s initializer calls a function defined
in another package that refers to <code>B</code>. in another package that refers to <code>B</code>.
</p> </p>
<p> <p>