Consider the following:
lizzy:~% cat r
require 'lib'
# from 'lib' import 'Foo'
f = Foo.new("string")
p f.bar
p 'abc'.rot13
lizzy:~% cat lib.rb
class String
def rot13
tr 'A-Za-z', 'N-ZA-Mn-za-m'
end
end
class Foo
def initialize(s)
@s = s
end
def bar
@s.rot13
end
end
lizzy:~% ruby r
"fgevat"
"nop"
lizzy:~%
What if I wanted String#rot13 to _not_ be visible in file `r' (because I
didn't ask for it)? How do I contain the change to String to lib.rb
only? Or
perhaps to Foo?
(The idea is to be able to reduce side-effects from require'ing files.
Python
forces one to be explicit about this, and that seems like a Good Thing.)
On a related note: how does one go about finding _where_ String#rot13
was
defined? One possible way would be for .methods to return an Array of
BoundMethods instead of Strings and have file and linenumber information
be
part of BoundMethod (if possible).
Thoughts?
on 19.01.2007 16:29
on 19.01.2007 16:31
Jos Backus schrieb: > (...) > What if I wanted String#rot13 to _not_ be visible in file `r' (because I > didn't ask for it)? How do I contain the change to String to lib.rb only? Or > perhaps to Foo? Jos, using this library http://raa.ruby-lang.org/project/import_module you can get the desired behaviour. The library hasn't been updated for some time, but it still seems to work with Ruby 1.8.5. I got some warnings, but it is easy to change the code to get rid of them. Regards, Pit
on 19.01.2007 16:31
On Jan 18, 2007, at 15:53, Jos Backus wrote: > lizzy:~% cat lib.rb > > `r' (because I > didn't ask for it)? How do I contain the change to String to > lib.rb only? Currently you can't. > Or perhaps to Foo? Define Foo#rot13 > On a related note: how does one go about finding _where_ > String#rot13 was > defined? One possible way would be for .methods to return an Array of > BoundMethods instead of Strings and have file and linenumber > information be > part of BoundMethod (if possible). > > Thoughts? grep? -- Eric Hodel - drbrain@segment7.net - http://blog.segment7.net I LIT YOUR GEM ON FIRE!
on 19.01.2007 18:18
Hi Eric, On Fri, Jan 19, 2007 at 05:03:16PM +0900, Eric Hodel wrote: > > > > @s = s > > > >What if I wanted String#rot13 to _not_ be visible in file > >`r' (because I > >didn't ask for it)? How do I contain the change to String to > >lib.rb only? > > Currently you can't. Right. I think it's important that the language supports this though. `require' can pollute the caller's code in uncontrollable ways, and it would be very useful to have control over this behavior. > > > >Thoughts? > > grep? Heh. The interpreter knows where it saw the most recent method definition; why not make this information available? Python, with its closed classes, doesn't have this "issue" because method definitions only live in one place per class. A colleague at work considers the fact that it's hard to find out where methods are defined a weakness, and I'd like to present him with a better answer than `grep'. It sounds like some more introspection could solve this "issue". Thanks,
on 19.01.2007 18:19
On Fri, Jan 19, 2007 at 06:40:03PM +0900, Pit Capitain wrote: > > you can get the desired behaviour. The library hasn't been updated for > some time, but it still seems to work with Ruby 1.8.5. I got some > warnings, but it is easy to change the code to get rid of them. Thanks Pit, I'll have a look and will report back.
on 20.01.2007 00:23
On Sat, Jan 20, 2007 at 02:18:19AM +0900, Jos Backus wrote: > > http://raa.ruby-lang.org/project/import_module > > > > you can get the desired behaviour. The library hasn't been updated for > > some time, but it still seems to work with Ruby 1.8.5. I got some > > warnings, but it is easy to change the code to get rid of them. > > Thanks Pit, I'll have a look and will report back. After looking at this I'm not sure how this helps me. Can you give an example please? I guess I wasn't describing the problem fully. As I see it, there are two issues: restricting imports (which import_module alledgedly addresses) and restricting exports. The earlier example causes String to be extended with .rot13 for everybody who includes `lib.rb'. I'd like there to be some way that String#rot13 would not be available outside `lib.rb' unless asked for. IOW, in addition to being able to control what is being imported it would be good to be able to control what is being exported. As an example, consider the following syntax. Its effect would be that String is only extended inside class Foo; references to String#rot13 outside of class Foo should fail. This would allow one to locally extend String without polluting String everywhere as is the case today. class Foo with class String def rot13 tr 'A-Za-z', 'N-ZA-Mn-za-m' end end def initialize(s) @s = s end def bar @s.rot13 end end As an aside, a while ago I ran into the fact that Process is a built-in class whereas I wanted my own class to be named Process instead, given that I wasn't going to use the built-in Process class anyway. Perhaps it would be better if Ruby made less classes available without require'ing them (maybe limited to a set of primitives). So to get what one has today one would say require 'core' or something like it. core in turn could be subdivided into useful subgroups.
on 20.01.2007 02:14
On 1/20/07, Jos Backus <jos@catnook.com> wrote: > On Sat, Jan 20, 2007 at 02:18:19AM +0900, Jos Backus wrote: > > On Fri, Jan 19, 2007 at 06:40:03PM +0900, Pit Capitain wrote: > > > Jos Backus schrieb: > > > >(...) [...] > > As an aside, a while ago I ran into the fact that Process is a built-in class > whereas I wanted my own class to be named Process instead, given that I wasn't > going to use the built-in Process class anyway. Maybe, you can copy the Process class to ProcessOriginal and then extend the Process class to fit your desired behavior. (I don't now how to "dup" a class : maybe the dup method do the job ; I haven't checked) > Perhaps it would be better if Ruby made less classes available without > require'ing them (maybe limited to a set of primitives). So to get what one > has today one would say > The ruby-core is already small. I think it's hard to split it in several sub groups and starting doing that is the first step to an unless sub grouping... Cheers,
on 20.01.2007 05:29
On Sat, Jan 20, 2007 at 10:13:15AM +0900, Nicolas Desprs wrote: > >class > >whereas I wanted my own class to be named Process instead, given that I > >wasn't > >going to use the built-in Process class anyway. > > Maybe, you can copy the Process class to ProcessOriginal and then > extend the Process class to fit your desired behavior. (I don't now > how to "dup" a class : maybe the dup method do the job ; I haven't > checked) Well, it's not there there is no workaround (I called it AProcess) but I shouldn't have to. I'd rather specify what I need in the first place, it avoids conflicts like these in the first place. Part of a possible solution would be to use modules as namespaces, e.g. Std::Kernel::Process. > >Perhaps it would be better if Ruby made less classes available without > >require'ing them (maybe limited to a set of primitives). So to get what one > >has today one would say > > > > The ruby-core is already small. I think it's hard to split it in > several sub groups and starting doing that is the first step to an > unless sub grouping... I don't agree. The Java core is much smaller; it perhaps would make for a good precedent/model to follow. Thanks for the input, Nicolas. Cheers,
on 20.01.2007 18:45
Quoting nicolas.despres@gmail.com, on Sat, Jan 20, 2007 at 10:13:15AM +0900: > >built-in class whereas I wanted my own class to be named Process > >instead, given that I wasn't going to use the built-in Process class > >anyway. So replace Process with your class: Process = Class.new # Causes a warning, as well it should, but you # want to do it anyhow... class Process #... end One day you may use a library that uses Process, and then you may regret this, it would be more typical to put your Process in a module: module MyModule class Process #... end class ProcessUser def use Process.new.increase_speed! end end end
on 20.01.2007 20:40
Jos Backus schrieb: > After looking at this I'm not sure how this helps me. Can you give an example > please? Jos, here's your original example using import-module: lib.rb: require "import-module" module LibStringEnhancements def rot13 tr 'A-Za-z', 'N-ZA-Mn-za-m' end end def within_lib String.import_module LibStringEnhancements do yield end end class Foo def initialize(s) @s = s end def bar within_lib do @s.rot13 end end end r.rb: require 'lib' f = Foo.new("string") p f.bar # => "fgevat" p 'abc'.rot13 rescue puts $! # => undefined method (...) within_lib do p 'abc'.rot13 # => "nop" end I haven't really used import-module yet, so I can't tell you much about it. The code above just shows how I would try to solve your problem. Feel free to ask again if this isn't what you wanted or if you have more questions. Regards, Pit
on 20.01.2007 21:52
On Sun, Jan 21, 2007 at 02:44:49AM +0900, Sam Roberts wrote: > So replace Process with your class: > > Process = Class.new # Causes a warning, as well it should, but you > # want to do it anyhow... > > class Process > #... > end Personally I think that's ugly. Except for some primitive classes I shouldn't even be able to access it unless I specifically ask for it, just like I don't get the Thread class unless I `require "thread"'. The idea is to reduce namespace pollution and possibly unwanted side-effects. > Process.new.increase_speed! > end > > end > > end While I agree that this is good practice, by default at least some of the currently-standard classes should be in their own module already. Perhaps all except for a number of primitive types. One could then access these other classes using something along the lines of `using namespace std;' in C++. Thanks for the feedback, Sam.
on 21.01.2007 00:02
On Sun, Jan 21, 2007 at 04:39:52AM +0900, Pit Capitain wrote: > Jos Backus schrieb: > >After looking at this I'm not sure how this helps me. Can you give an > >example > >please? > > Jos, here's your original example using import-module: > [great example snipped] > I haven't really used import-module yet, so I can't tell you much about > it. The code above just shows how I would try to solve your problem. > > Feel free to ask again if this isn't what you wanted or if you have more > questions. That's a great example. I understand how this could help much better now. But I'm not sure how well this would scale if you had multiple classes/modules/methods you'd want to restrict this way. Also, turning rot13 into a module method doesn't look like the most natural way to me because I'd really like to be able to just add it to String. And having to add it to each method call, while good as far a granularity goes, will cause a lot of extra work, which is why my example used a class-based approach. Perhaps what I'm really looking for is a way to tag classes and methods for conditional export, such that they are only available in other classes if specifically imported. This will almost certainly not be possible without some language changes. The background behind this is that I'm trying to sell Ruby at work as a viable alternative to Perl/Python, and this lack of explicit import/export controls is seen as a weakness. As one colleague put it "How do I find out where rot13 is defined? Finding it in Python is straightforward (because classes and functions have to imported explicitly) but in Ruby it is not. This poses a long-term readability problem.". (My apologies for the poor description of the issue as I see it. I'm clearly not a language designer.) Thanks for your excellent (as always) suggestions, Pit.
on 21.01.2007 00:20
Jos Backus schrieb: > (...) > The background behind this is that I'm trying to sell Ruby at work as a viable > alternative to Perl/Python, and this lack of explicit import/export controls > is seen as a weakness. As one colleague put it "How do I find out where rot13 > is defined? Finding it in Python is straightforward (because classes and > functions have to imported explicitly) but in Ruby it is not. This poses a > long-term readability problem.". Jos, just out of curiosity, could you show us (or me via private mail, if you think it's too offtopic for ruby-core) how you would do what you want in Python? Regards, Pit
on 21.01.2007 00:31
On Sun, Jan 21, 2007 at 08:19:47AM +0900, Pit Capitain wrote: > Jos, just out of curiosity, could you show us (or me via private mail, > if you think it's too offtopic for ruby-core) how you would do what you > want in Python? Pit, I'm really sorry but I'm not well-versed enough in Python to be able to demonstrate this. I've cc'd my colleague and will ask him for an example. Thanks!
on 21.01.2007 01:02
On Jan 20, 2007, at 3:31 PM, Jos Backus wrote:
> example.
I'm that colleague. I'm not the world's best Python expert either,
but I think the answer is that "you don't".
That is, you don't extend the String class to add a rot13 method,
because Python doesn't have open classes like that. (I think it can
possibly be done, but only awkwardly; the language does not encourage
it.)
If you want to provide rot13 functionaliy, you'll put it in a
function in a package that does it. Anyone who wants to rot13 will
explicitly import your package.
The point is, in Python, if file A chooses to import package B, it is
very unlikely to affect code operating in completely unrelated file
C. In Ruby, it's entirely possible that the import of B could have
broken something that C depended on.
If you are in a world where all the code is written by good,
competent programmers who've agreed on the ground rules, Ruby can
give them all a lot of power. But if you're not so sure about your
fellow programmers, or about code that you are loading off the net,
then Ruby's model offers much less protection than Python's.
This kind of thing would make me much more scared to work on a big
project in Ruby than I would be in Python. (I haven't yet had the
opportunity to work on a big project in either, so I'm not actually
talking from experience.)
- Terry
on 21.01.2007 19:58
Hi Terry, On 1/20/07, Terry Weissman <terry@liveops.com> wrote: > The point is, in Python, if file A chooses to import package B, it is > very unlikely to affect code operating in completely unrelated file > C. In Ruby, it's entirely possible that the import of B could have > broken something that C depended on. As you pointed out, this is a trade off between having a powerful language and having a safe one. Most languages that I know won't let me redefine the + operator for integers. I really like that Ruby lets me do it. (I haven't needed to redefine +, at least not yet.) I can imagine a reasonable language that has the features you'd like. I just wouldn't call it Ruby. I'm glad Matz chose to position Ruby much more towards the "powerful" end of the spectrum rather than the "safe" end. There are domains that can benefit from a lot of safety, and others that can benefit from a lot of power. If I were programming the control system for a nuclear reactor, Ruby probably wouldn't be my first choice. But that's one reason I don't do those kinds of projects. > If you are in a world where all the code is written by good, > competent programmers who've agreed on the ground rules, Ruby can > give them all a lot of power. But if you're not so sure about your > fellow programmers, or about code that you are loading off the net, > then Ruby's model offers much less protection than Python's. > > This kind of thing would make me much more scared to work on a big > project in Ruby than I would be in Python. I wouldn't want to program a large project in Ruby with programmers that I didn't trust. But having been on projects like this using "safer" languages, I also wouldn't want to program a large project in _any_ language with programmers that I didn't trust. There are just so many ways another programmer can make a project difficult, and any language can only protect you from a handful of them. (Hi Jos! How are you doing?) Wayne --- Wayne Vucenic No Bugs Software Ruby, C#, and Erlang Agile Contract Programming in Silicon Valley
on 21.01.2007 21:39
Hi Wayne, On Mon, Jan 22, 2007 at 03:57:59AM +0900, Wayne Vucenic wrote: > Hi Terry, > > On 1/20/07, Terry Weissman <terry@liveops.com> wrote: > >The point is, in Python, if file A chooses to import package B, it is > >very unlikely to affect code operating in completely unrelated file > >C. In Ruby, it's entirely possible that the import of B could have > >broken something that C depended on. A typical example (correct me if I'm wrong) would be for somebody to override an existing method, introducing differing behavior whereas you'd be relying on the behavior of the original method. E.g.: lizzy:/tmp% cat x class Foo def bar puts "hi" end end class Foo def bar puts "ho" end end Foo.new.bar lizzy:/tmp% ruby x ho lizzy:/tmp% In Ruby's defense, there's: lizzy:/tmp% ruby -w x x:8: warning: method redefined; discarding old bar ho lizzy:/tmp% To make this more useful, one of my proposals is to augment this information such that the interpreter would be able to tell you where it found the old definition: lizzy:/tmp% ruby -w x x:8: warning: method redefined; discarding old bar at x:2 ho lizzy:/tmp% This would be helpful when trying to figure out where methods are defined. Similarly: lizzy:/tmp% cat x FOO = 1 FOO = 2 lizzy:/tmp% ruby x x:2: warning: already initialized constant FOO at x:1 lizzy:/tmp% > that can benefit from a lot of safety, and others that can benefit > >This kind of thing would make me much more scared to work on a big > >project in Ruby than I would be in Python. > > I wouldn't want to program a large project in Ruby with programmers > that I didn't trust. But having been on projects like this using > "safer" languages, I also wouldn't want to program a large project in > _any_ language with programmers that I didn't trust. There are just > so many ways another programmer can make a project difficult, and any > language can only protect you from a handful of them. > True. But I still wonder if some compromise wouldn't be possible through the use of scoping control. > (Hi Jos! How are you doing?) Doing fine :-) *waves*