Method annotation and anonymous functions

S

stevetuckner

I want to start a discussion about two things that Matz talked about at
the rubycon. Dave Thomas suggested that we could get Matz' anonymous
function by using "def" without a name. Others suggested that if def
returned a method object, then annotations could be applied to that
object. So here goes my suggestions. As they stand, they are not even
parseable by Ruby, but lets start talking about it anyway.

Anonymous functions:

x = def (a=1,b=2) { ... }
y = def(a,b=3) begin
.....
end

Annotated functions:

class A
def foo(a, b)
.visibility :private # how can this be parsed? (the above
line has no trailing marker to signal it as one expression
.returns Integer
begin
....
end

def foo2(a, b).visibility(private).returns(Integer) { ... }

f3 = def foo3(a,b)
end

f3.visibility:)private).returns(Integer)

Anonymous, Annotated functions

x = def (a, b).visibility(private).returns(Integer) { ... }
y = def (a, b) { ... }
y.visibility:)private) # apply annotations later
y.returns:)integer)

where for anonation examples:

class Method
def visibility(v)
@annotations[:visibility] = v
return self
end

def returns(r)
@annotations[:returns] = v
return self
end
end

Please rake me over the coals because I am so ignorant ;-)

Steve Tuckner
 
P

Paul Brannan

I want to start a discussion about two things that Matz talked about at
def foo2(a, b).visibility(private).returns(Integer) { ... }

I think what most people have in mind is something like this:

private def foo(a, b, c)
# ...
end

private module_function def bar(a, b, c)
# ...
end

Chaining the annotations on multiple lines can be done using '\':

private \
module_function \
doc("This is a docstring") \
def baz(a, b, c)
# ...
end

This also requires changine private() and module_function() to return a
Symbol rather than a Module (i.e. to return their parameter rather than
self).

See RCR#277 for more information.

Paul
 
D

David A. Black

Hi --

I want to start a discussion about two things that Matz talked about at the
rubycon. Dave Thomas suggested that we could get Matz' anonymous function by
using "def" without a name. Others suggested that if def returned a method
object, then annotations could be applied to that object. So here goes my
suggestions. As they stand, they are not even parseable by Ruby, but lets
start talking about it anyway.

Anonymous functions:

x = def (a=1,b=2) { ... }
y = def(a,b=3) begin
.....
end

I don't think either of those is exactly what Dave suggested:

x = def (a=1,b=2)
...
end

without the braces, and without 'begin', just like a method
definition.

I actually like the second iteration of the idea more:

x = lambda (a=1,b=2)
...
end

using method-definition style for an anonymous function, but a
different keyword. This keeps it more robust; there's a more "solid"
indication that it's something other than a def.

(Actually that was sort of the third iteration :) The first was:

-> (a=1,b=2)
...
end

which I posted here last week in an attempt to at least ameliorate the
arrow thing. But having a real keyword plus the no-braces is
definitely better.)



David
 
T

Trans

I just finished (are we ever "finished"?) an annotations system that is
being used by Nitro. Originally I created a class I called a Service
(bad name I know) to manage first class methods so I could annotate
them. It worked okay, but had a lot of overhead. Yet there was one
really bad problem: what about *reusable annotations*? You need to be
able to create annotations w/o necessarily having a method to attach
them to.

I tried a VirtualService but it was even worse. To remedy I decided to
use Symbol instead since we typically use those to reference methods
(perhaps even better would be a sublclass MethodName < Symbol).
Unfortunately without a built in Binding.of_caller it's not very good
either :-(. So I ended up just storing them in a hash in the
class/module they belong to.

Anyway, the point is that annotations are better if they can exist on
their own too --as _potental annotations_, if you will. Something to
consider.

T.
 
S

stevetuckner

--------------060708070505030300090705
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Paul said:
I think what most people have in mind is something like this:

private def foo(a, b, c)
# ...
end

private module_function def bar(a, b, c)
# ...
end
What I don't like about that proposal is that these annotations only
take one argument (the symbol) and aren't applied to a Method object
(where the annotations rightly should reside).
Chaining the annotations on multiple lines can be done using '\':

private \
module_function \
doc("This is a docstring") \
def baz(a, b, c)
# ...
end
Aside from looking icky, how does doc() know what symbol to use to apply
the annotation with?
This also requires changine private() and module_function() to return a
Symbol rather than a Module (i.e. to return their parameter rather than
self).

See RCR#277 for more information.

Paul


--------------060708070505030300090705--
 
S

stevetuckner

David said:
Hi --




I don't think either of those is exactly what Dave suggested:

x = def (a=1,b=2)
...
end

What I don't like about this is that to do one liners, you need to do:

x = def(a=1, b=2); ...; end
without the braces, and without 'begin', just like a method
definition.

I actually like the second iteration of the idea more:

x = lambda (a=1,b=2)
...
end

same issue
using method-definition style for an anonymous function, but a
different keyword. This keeps it more robust; there's a more "solid"
indication that it's something other than a def.

I don't mind lambda over def. What object would lambda return? A proc
object? Could annotations be applied to it?
 
S

stevetuckner

Trans said:
I just finished (are we ever "finished"?) an annotations system that is
being used by Nitro. Originally I created a class I called a Service
(bad name I know) to manage first class methods so I could annotate
them. It worked okay, but had a lot of overhead. Yet there was one
really bad problem: what about *reusable annotations*? You need to be
able to create annotations w/o necessarily having a method to attach
them to.

I tried a VirtualService but it was even worse. To remedy I decided to
use Symbol instead since we typically use those to reference methods
(perhaps even better would be a sublclass MethodName < Symbol).
Unfortunately without a built in Binding.of_caller it's not very good
either :-(. So I ended up just storing them in a hash in the
class/module they belong to.

Anyway, the point is that annotations are better if they can exist on
their own too --as _potental annotations_, if you will. Something to
consider.

T.
From the replies I got so far, it seems as though "clean solutions"
involving method objects are too inefficient. Though I don't know why or
even if it is so. Can you post some code showing your annotations
addition you did for Nitro?

Steve Tuckner
 
P

Paul Brannan

I don't think either of those is exactly what Dave suggested:

x = def (a=1,b=2)
...
end

without the braces, and without 'begin', just like a method
definition.

I actually like the second iteration of the idea more:

x = lambda (a=1,b=2)
...
end

I like this too. And there's no reason IMO why we can't have both
anonymous functions and anonymous closures (so def and lambda could have
the same syntax but lambda would create a closure, while def would not).

Then we could also have named closures as well:

class Foo
a = 42
lambda foo(b, c)
puts a, b, c
end
end

The only problem I have with this is that currently lambda is not a
keyword; it is a method that takes a block and returns a Proc object.
Creating new keywords, especially out of existing non-keywords, should
always be done with caution. Currently, the following code works fine:

def lambda(article)
puts "Hello from [ruby-talk:#{article}]"
end

article = 161640
lambda(article) #=> Hello from [ruby-talk:161460]

But I would imagine that if lambda were a keyword this would confuse the
parser.

IMO the real issue is that we want to distinguish between blocks that
take arguments by multiple assignment and blocks that take arguments the
"usual" way. One way to do this, then, is to use similar syntax for
both kinds of blocks. For example, consider:

foo do |a, b|
# arguments passed by multiple assignment. matz has previously
# indicated that in the future a and b will also be block-local.
end

vs:

foo do <a, b>
# arguments passed as a function. a and b are block-local
# regardless of whether they have been previously defined.
end

p = proc { <a, b> ... a and b are not passed by MI here ... }

foo do <&block>
# an anonymous function that takes a block. you can pass the block
# from inside foo() with:
# yield { ... }
end

This is actually the same syntax that matz proposed in
[ruby-talk:12289] for declaring paramters to be block-local; I'm
extending the idea by suggesting that the angle brackets also change how
the arguments are passed. IIRC there was much controversy over the
<...> syntax at that time, so please don't get too hung up on the syntax
here; consider instead the idea of differentiating between how the
parameters are passed based on some specification of the parameter list
rather than the function itself.

Paul
 
P

Paul Brannan

What I don't like about that proposal is that these annotations only
take one argument (the symbol) and aren't applied to a Method object
(where the annotations rightly should reside).

Changing def to return a Method object could also be done, but this is a
larger change; it requires methods like private() to be able to take a
Method or a Symbol (currently they only take a Symbol).

The annotation does not belong with the (Unbound)Method object; it
belongs with the method. This is an important distinction:

irb(main):001:0> class Foo; def foo; end; end
=> nil
irb(main):005:0> m1 = Foo.instance_method:)foo); m1.object_id
=> 538483812
irb(main):006:0> m2 = Foo.instance_method:)foo); m2.object_id
=> 538477272

Note that m1 and m2 are two *different* objects which refer to the same
method.
Aside from looking icky, how does doc() know what symbol to use to apply
the annotation with?

Good point, I missed this the first time through.

Paul
 
S

Sean O'Halpin

I like this too. And there's no reason IMO why we can't have both
anonymous functions and anonymous closures (so def and lambda could have
the same syntax but lambda would create a closure, while def would not).

I agree. See the thread "Anonymous methods, blocks etc. (Cont.
'default block params')" starting at ruby-talk/160709 for some recent
discussion about this.
Then we could also have named closures as well:

class Foo
a =3D 42
lambda foo(b, c)
puts a, b, c
end
end

That's an interesting idea. It would certainly be orthogonal. However,
you can already do that with define_method:

class Foo
a =3D 42
define_method:)foo) do |b, c|
puts a, b, c
end
end

Foo.new.foo(1,2)
__END__
42
1
2
The only problem I have with this is that currently lambda is not a
keyword; it is a method that takes a block and returns a Proc object.
Creating new keywords, especially out of existing non-keywords, should
always be done with caution.
Agreed.

Currently, the following code works fine: [snip]
But I would imagine that if lambda were a keyword this would confuse the
parser.

Well, I doubt it would break ~much~ code. It would be quite an unusual
thing to define a function named lamdba(), no?

[snip discussion about || vs <> block local syntax]

I think you're on a losing wicket here. It seems that the zeitgeist is
with the semi-colon to introduce block locals.

Regards,

Sean
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: Method annotation and anonymous functions"

|Anonymous functions:
|
|x = def (a=1,b=2) { ... }
|y = def(a,b=3) begin
| .....
|end

Probably I will not use "def" for anonymous function, because there
are tow major differences in def statement and anonymous def above:

* def statement defines new name for a method, anonymous def does
not define any name.

* besides that, def statement introduces a whole new scope,
anonymous def should be a closure, that allows access to external
local variables.

|Annotated functions:
|
|class A
| def foo(a, b)
| .visibility :private # how can this be parsed? (the above
|line has no trailing marker to signal it as one expression
| .returns Integer
| begin
| ....
| end

This is a too big syntactic incompatibility.

matz.
 
J

Jeff Wood

Most languages define some token that specifically states method attribute.=
..
Java uses @... since @ isn't a used char otherwise.

for us ... it would need to be something else ...

and it would simply bind to the function its declared in, just like
functions bind to the classes they are declared in.

how 'bout

def method1( a, b )
->visibility =3D true
->returns =3D int
# actually do some work...
...
end

just thinking out loud... since it would have a new punctuation it
wouldn't introduce incompatibility.

j.

Hi,

In message "Re: Method annotation and anonymous functions"
 
J

Jeff Wood

Of course, my use of '->' doesn't take into consideration the
suggestions for new style syntax for blocks ->( a, b ) { ... }

...

You've always done a great job with things, I guess I'm just
suggesting another bit of punctuation that would state ( attribute of
surrounding method ).

j.

Most languages define some token that specifically states method attribut= e...
Java uses @... since @ isn't a used char otherwise.

for us ... it would need to be something else ...

and it would simply bind to the function its declared in, just like
functions bind to the classes they are declared in.

how 'bout

def method1( a, b )
->visibility =3D true
->returns =3D int
# actually do some work...
...
end

just thinking out loud... since it would have a new punctuation it
wouldn't introduce incompatibility.

j.

Hi,

In message "Re: Method annotation and anonymous functions"
 
D

David A. Black

Hi --

Most languages define some token that specifically states method attribute...
Java uses @... since @ isn't a used char otherwise.

for us ... it would need to be something else ...

and it would simply bind to the function its declared in, just like
functions bind to the classes they are declared in.

how 'bout

def method1( a, b )
->visibility = true
->returns = int
# actually do some work...
...
end

just thinking out loud... since it would have a new punctuation it
wouldn't introduce incompatibility.

Worse: it would introduce new punctuation :)


David
 
J

Jeff Wood

Yes it would, but I don't see another way to do attribution of methods
without causing compatibility issues .... do you?

j.
 
T

Trans

Jeff said:
Yes it would, but I don't see another way to do attribution of methods
without causing compatibility issues .... do you?

Nitro/Facets annotations system works like so:

class X
def foo ; "foo" ; end
ann :foo, :returns => String
end

X.ann :foo, :log => true

X.ann.foo.returns #=> String
X.ann.foo.log #=> true

Also,

class X
attr :bar, :log => true
end

Would a special syntax be better? Maybe, but not neccessary.

T.
 
D

David A. Black

Hi --

Yes it would, but I don't see another way to do attribution of methods
without causing compatibility issues .... do you?

I think I'm coming from a different perspective. I haven't seen any
examples of this that make me think it's worth either incompatibility
or new punctuation. I strongly dislike the idea of having a
specification of the class of the return value (object) of a method,
since that militates against duck typing. But maybe that's just an
example.


David
 
D

David A. Black

Hi --

Nitro/Facets annotations system works like so:

class X
def foo ; "foo" ; end
ann :foo, :returns => String
end

X.ann :foo, :log => true

X.ann.foo.returns #=> String

Ugh, if I may permit myself the expression That means goodbye to
things like:

def meth(thing)
thing[some_thing_else]
end

Pinning down the class of return values is similar to pinning down the
class of arguments -- arguably even more duck-typing-unfriendly, since
you're specifying not only what a given object has to be but,
potentially, what *its* methods have to return.


David
 
J

Jeff Wood

... the attributes have NOTHING to do with duck typing... that was
simply an example chosen by somebody else.

I would be using the attributes for testing ...

def mytest_of_doom()
ann :doc "This function does some blah with blah."
ann :test
ann :group :working, :ui
# do stuff
end

then you can do things like, I want a list of all functions that are
tests. or all functions that are part of group <x> ... or, even
better, run-time functionality for getting information on a function
while in irb ... ( think about the help() function in python ).

Those would all be killer uses of attributed functions.

the type thing was simply an example. and notice it had nothing to do
with input params ( still whatever you want ). but, it does let you
know what the function will spit ( and that helps ides and editors
since now they can provide you with a list of methods after the
chain... )

It's really a VERY cool idea. I really hope it makes it in. I know
most people seem to hate adding a little punctuation ... but sometimes
it can be a good thing.

j.

Hi --

Nitro/Facets annotations system works like so:

class X
def foo ; "foo" ; end
ann :foo, :returns =3D> String
end

X.ann :foo, :log =3D> true

X.ann.foo.returns #=3D> String

Ugh, if I may permit myself the expression That means goodbye to
things like:

def meth(thing)
thing[some_thing_else]
end

Pinning down the class of return values is similar to pinning down the
class of arguments -- arguably even more duck-typing-unfriendly, since
you're specifying not only what a given object has to be but,
potentially, what *its* methods have to return.


David
 
E

Eric Mahurin

--- "David A. Black said:
Hi --
=20


Nitro/Facets annotations system works like so:

class X
def foo ; "foo" ; end
ann :foo, :returns =3D> String
end

X.ann :foo, :log =3D> true

X.ann.foo.returns #=3D> String
=20
Ugh, if I may permit myself the expression That means
goodbye to
things like:
=20
def meth(thing)
thing[some_thing_else]
end
=20
Pinning down the class of return values is similar to pinning
down the
class of arguments -- arguably even more
duck-typing-unfriendly, since
you're specifying not only what a given object has to be but,
potentially, what *its* methods have to return.[/QUOTE]

I'll be the devil's advocate... A good use of something like
this would be if we had a VM that had type inference (like the
self VM). Where it had trouble inferring the type (pretty much
a class - to get the exact methods at compile time), giving the
class of the args and return value would help it.

But of course the best solution is to improve the type
inference engine (if we had one). Ruby is a dynamically typed
language and I think it should stay that way. Let the VM guys
continually do optimizations instead of dirting the language
with static-type hints. I'd be afraid of too many people
abusing it and disabling the power and beauty of duck-typing.




=09
__________________________________=20
Yahoo! Music Unlimited=20
Access over 1 million songs. Try it free.
http://music.yahoo.com/unlimited/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,831
Latest member
RusselWill

Latest Threads

Top