Duck Typing and automated Conversions

F

Florian Gross

itsme213 said:
Florian Gross' Binding.of_caller could probably be used to prototype ideas
like this, but it would take someone considerably smarter than me (Florian?)
to explore this :)

I fear I'm not smart enough to understand all this pattern matching
stuff just yet. But I'd certainly be willing to help out with getting
Binding.of_caller to work.
 
M

Mark Hubbart

Hi,

I think I probably was somewhat unclear in my other post. I wasn't
arguing for or against your idea (for the most part), I was going off
it and thinking up a related idea... I guess it was actually pretty
off topic.

Well, then you'll learn that your instance must implement to_int. Same as
if someone had coded that directly in the method. I don't see this as an
argument against my suggested shortcut syntax, because that mainly affects
the *writer* of a method - not the *caller*.

My only argument against the shortcut syntax is that I can't see how
often it could be used, except to simulate static typing. I suspect
that adding a feature like this would encourage the use of static
typing in methods.

In your example method, you called #to_int on an object that you were
just going to call #times on. This doesn't guarantee that you will get
an integer, and makes it difficult for others to use duck typing in
code that interfaces with yours. In fact, if they need the
functionality, then the have to resort to some hack like this:

obj = ObjectThatImplementsTimes.new
wrapper = Object.new
class << wrapper;self;end.__send__:)define_method, :to_int){obj}

A very ugly hack, just to re-allow duck typing.
You introduced the #respond_to? - my suggestion was to simply invoke it and
see what happens (duck typing).

I was describing something else; I was describing a possible problem
with *my* implementation of a related idea. OT, I know :/
Sure. I'm very open here. I think the selection of "." as separator was
not good. We can change that any time. But the real issue for me is, does
this make sense independend of syntax?

I still think that this is, for the most part, an implementation of
static typing. It may just be me, but I very rarely use to_str,
to_int, etc, in my code. The only time I do is when I absolutely have
to have those particular classes. Which is so uncommon for me, I can't
think of the last time I used it. But as I said, I may very well be in
the minority here.

cheers,
Mark
 
R

Robert Klemme

itsme213 said:
Thank you. I may have stated too strongly, and you may have read more into
it.

Yeah, that's likely.
It's not a central part of your scheme (or my generalization). Here is
my re-explanation:

First, let's limit the scope: In any object graph just about any object
can
be accessible from anywhere (via transitive returns from other methods,
aliasing, globals, and constants like ObjectSpace). So in that extreme,
just
about any method can be invoked on just about any object. We are not about
address aliasing, so let's just focus on those objects that are passed
back
and forth via method parameters.

For these objects, your scheme allows us to say that the method body only
has named references to things returned by methods on the object. Which
means that it will not have references to the objects themselves (unless
those methods on the objects themselves lead there, or it uses other paths
through the object graph, such as via ObjectSpace).

Any better?

Definitely! But IMHO that weakened the property so much that I ask myself
whether it's still useful. :)

Cheers

robert
 
R

Robert Klemme

Mark Hubbart said:
Hi,

I think I probably was somewhat unclear in my other post. I wasn't
arguing for or against your idea (for the most part), I was going off
it and thinking up a related idea... I guess it was actually pretty
off topic.

Ah, ok, I see much clearer now. Thanks for clarifying.
My only argument against the shortcut syntax is that I can't see how
often it could be used, except to simulate static typing. I suspect
that adding a feature like this would encourage the use of static
typing in methods.

Ok, then we need a poll. Or we should dig into Ruby's std lib to see how
often these methods are called.
In your example method, you called #to_int on an object that you were
just going to call #times on. This doesn't guarantee that you will get
an integer,

Of course.
and makes it difficult for others to use duck typing in
code that interfaces with yours.

The same holds true if the idiom is used inside the method body.
In fact, if they need the
functionality, then the have to resort to some hack like this:

obj = ObjectThatImplementsTimes.new
wrapper = Object.new
class << wrapper;self;end.__send__:)define_method, :to_int){obj}

A very ugly hack, just to re-allow duck typing.

The invocation of to_int is already and application of duck typing.

I still think that this is, for the most part, an implementation of
static typing. It may just be me, but I very rarely use to_str,
to_int, etc, in my code. The only time I do is when I absolutely have
to have those particular classes. Which is so uncommon for me, I can't
think of the last time I used it. But as I said, I may very well be in
the minority here.

We need figures here. If I find the time this weekend I'll try to come up
with statistics.

robert
 
D

David A. Black

Hi --

Ok, then we need a poll. Or we should dig into Ruby's std lib to see how
often these methods are called.

Not very :) For 1.8.2:

$ grep '\.to_str' `find . -name "*.rb"` | grep -v to_string | wc -l
6
$ grep '\.to_int' `find . -name "*.rb"` | wc -l
1

(It's around 700 for to_s and 300 for to_i.)
The invocation of to_int is already and application of duck typing.

OK, I think I've clarified my big problem with all of this:

How would you move this call to #join to a method signature?

def meth(a)
puts a.join(", ")
end

I think there are only two ways:

1. put the whole thing, including the ", ", into the signature
(which I doubt anyone would advocate);
2. back away from duck typing and rely on something else, probably
#respond_to? You're then dealing with the capabilities of the
object at one level of remove -- which, of course, is something
one often does, but it's only a subset of available techniques
for dealing with type and object capability. (My position is
that duck-typing - #respond_to? > 0)

In other words, I question whether it's possible to re-model runtime
message-sending as a method signature, without some kind of major
reconception of the whole thing and/or up-front decision to drop
one or more programming techniques.


David
 
F

Florian Gross

itsme213 said:
Looks like it uses eval, which only takes a string, so I'm not sure ... Do
you think it could implement bind_vars to do the following?

def f (arr)
bind_vars [:x, :y, :z], arr
[x, y, z]
end

Mostly. But you would need to initialize the variables x, y and z to nil
so that Ruby actually sees them as variables and not method calls.

It would work like this:

def bind_vars(names, values)
Binding.of_caller do |context|
setter = eval("lambda { |#{names.join(", ")}| }", context)
setter.call(*values)
end
end
 
D

David A. Black

Hi --

But you need no advocating to do, even today:
def meth (a, b = a.join(","))
b
end

The only difference that 'b' is a variable with a default binding, which the
caller can replace.

True?

Yes -- a big difference, in my view. Mainly I meant that there's is,
I think, probably not a lot of advocacy of putting arbitrarily much of
a method into its signature. (Maybe I'm just old-fashioned though :)


David
 
R

Robert Klemme

David A. Black said:
Hi --



Not very :) For 1.8.2:

$ grep '\.to_str' `find . -name "*.rb"` | grep -v to_string | wc -l
6
$ grep '\.to_int' `find . -name "*.rb"` | wc -l
1

(It's around 700 for to_s and 300 for to_i.)

Here's my stat of ruby 1.8.2:

$ ruby -e 'Dir["**/*.{h,cC,rb,rw}"].inject(Hash.new(0)){|h,f|
File.open(f){|io| io.each{|li|
li.scan(/to_\w+/){|m|h[m]+=1}} };h}.sort.each{|k,v| print k," ",v,"\n"}'
to_a 193
to_above 2
to_access_path 1
to_addr 3
to_addrs 8
to_ajd 13
to_all 2
to_amjd 2
to_append 2
to_arry 5
to_ary 26
to_asn1integer 1
to_backslash 3
to_backslash_sequence 11
to_be_linked 1
to_below 2
to_bitmap 5
to_bn 1
to_bold 4
to_built_in_class 1
to_cache 4
to_check 5
to_civil 8
to_clean_up 6
to_clone 2
to_closest 2
to_commercial 4
to_consume 2
to_coords 2
to_d 6
to_date 2
to_day_fraction 3
to_dbl 2
to_dc_date 2
to_default 6
to_del 6
to_delete 2
to_der 51
to_der_if_possible 1
to_diff 3
to_digits 1
to_dir 4
to_directory_uri 2
to_e 2
to_enc 15
to_enclosed 2
to_euc_jp_from_iso_2022_jp 1
to_euc_jp_from_shift_jis 1
to_euc_jp_from_utf_8 1
to_eval 34
to_external 2
to_f 88
to_file 7
to_filename 5
to_find 3
to_flow 8
to_h 3
to_hash 21
to_hash_kv 8
to_html 5
to_i 541
to_ico 1
to_id 10
to_idx 2
to_indent_mode 4
to_index 4
to_int 3
to_internal 3
to_inum 2
to_io 10
to_iso_2022_jp_from_euc_jp 1
to_iso_8859_15 1
to_iso_8859_1_from_utf_8 1
to_jd 24
to_key 7
to_key_and_deref 1
to_label 2
to_latex 1
to_ld 2
to_level 6
to_line 1
to_lines 4
to_list 8
to_load 13
to_mailtext 3
to_mediator 12
to_mjd 2
to_name 6
to_new_year 2
to_newline 3
to_num 1
to_obj 26
to_oldpath 2
to_one 1
to_ordinal 5
to_overlapping 2
to_path 7
to_pem 9
to_pixel 6
to_png 3
to_pointer 5
to_proc 23
to_ptr 6
to_r 31
to_re 2
to_replace 4
to_rfc822text 1
to_rjd 1
to_router 5
to_rss 59
to_run 8
to_s 1379
to_s_09 1
to_s_10 1
to_s_20 1
to_s__ 2
to_s_table 2
to_s_with_iv 1
to_s_without_iv 1
to_search 2
to_set 9
to_shell 3
to_shift_jis_from_euc_jp 1
to_shift_jis_from_utf_8 1
to_src 2
to_str 32
to_string 27
to_sym 5
to_table 5
to_taskbar 2
to_text 7
to_time 9
to_time_t 1
to_uri 1
to_utc 4
to_utf 8
to_utf_8_from_euc_jp 1
to_utf_8_from_iso_8859_1 1
to_utf_8_from_shift_jis 1
to_version 5
to_wday 2
to_withtag 2
to_xml 4
to_xpath_helper 3
to_yaml 75
to_yaml_fold 3
to_yaml_properties 13
to_yaml_type 21

Sum 3143

Top counts

1379 to_s
541 to_i
193 to_a
88 to_f
75 to_yaml
59 to_rss
51 to_der
34 to_eval
32 to_str
31 to_r
27 to_string
26 to_ary
26 to_obj
24 to_jd
23 to_proc
21 to_hash
21 to_yaml_type

Kind regards

robert
 
D

David A. Black

Hi --

David A. Black said:
Hi --



Not very :) For 1.8.2:

$ grep '\.to_str' `find . -name "*.rb"` | grep -v to_string | wc -l
6
$ grep '\.to_int' `find . -name "*.rb"` | wc -l
1

(It's around 700 for to_s and 300 for to_i.)

Here's my stat of ruby 1.8.2:

$ ruby -e 'Dir["**/*.{h,cC,rb,rw}"].inject(Hash.new(0)){|h,f|
File.open(f){|io| io.each{|li| li.scan(/to_\w+/){|m|h[m]+=1}}
};h}.sort.each{|k,v| print k," ",v,"\n"}'
to_a 193
to_above 2
to_access_path 1
to_addr 3
[...]

I would tend to want to match /\.to_\w+/, though I guess some things
have embedded to's (like pixel_to_coords). Also, some of the ones
you've found are in comments, such as secs_to_new_year, which is given
in the documentation as an example of something one might write with
DateTime, so it really doesn't have much in common with to_str or
to_int at the language level.

(I do love grepping the lib, I must admit :)


David
 
R

Robert Klemme

David A. Black said:
Hi --

David A. Black said:
Hi --

On Sat, 15 Jan 2005, Robert Klemme wrote:



My only argument against the shortcut syntax is that I can't see how
often it could be used, except to simulate static typing. I suspect
that adding a feature like this would encourage the use of static
typing in methods.

Ok, then we need a poll. Or we should dig into Ruby's std lib to see how
often these methods are called.

Not very :) For 1.8.2:

$ grep '\.to_str' `find . -name "*.rb"` | grep -v to_string | wc -l
6
$ grep '\.to_int' `find . -name "*.rb"` | wc -l
1

(It's around 700 for to_s and 300 for to_i.)

Here's my stat of ruby 1.8.2:

$ ruby -e 'Dir["**/*.{h,cC,rb,rw}"].inject(Hash.new(0)){|h,f|
File.open(f){|io| io.each{|li| li.scan(/to_\w+/){|m|h[m]+=1}}
};h}.sort.each{|k,v| print k," ",v,"\n"}'
to_a 193
to_above 2
to_access_path 1
to_addr 3
[...]

I would tend to want to match /\.to_\w+/, though I guess some things
have embedded to's (like pixel_to_coords).

But that would not catch cases like def foo() to_s end...
Also, some of the ones
you've found are in comments, such as secs_to_new_year, which is given
in the documentation as an example of something one might write with
DateTime, so it really doesn't have much in common with to_str or
to_int at the language level.

Yeah, it's quite imperfect. I should have used \b to match word
boundaries. Still that would catch comments....
(I do love grepping the lib, I must admit :)

Apparently. :)

Regards

robert
 
C

Curt Sampson

obj = Object.new
def obj.times; 10; end
str = silly_example("hello", obj)

Here you have an object that responds to 'times', so it's capable of
being treated in a duck-typing way by simply being sent the message
'times' -- but if you intercept the object and do a to_int on it,
there's trouble.

(Of course you could document your method by saying that it requires
an object that responds to to_int, but that seems a bit roundabout
compared to just saying you want an object that responds to times.)

I don't see why there would be a need to add documentation indicating
that count must respond to times, since it's already written right there
in the code. We just need a type inference engine to find this and tell
us about it.

cjs
 
R

Robert Klemme

Curt Sampson said:
I don't see why there would be a need to add documentation indicating
that count must respond to times, since it's already written right there
in the code. We just need a type inference engine to find this and tell
us about it.

That would be a completely different language. I guess you're thinking of
some functional languages...

Regards

robert
 

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

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,739
Latest member
Clint8040

Latest Threads

Top