From: zuzu Date: 2004-12-12T09:57:15+09:00 Subject: Re: puts / print as method not keyword? On Sun, 12 Dec 2004 09:03:54 +0900, Ilmari Heikkinen wrote: > > > > On 12.12.2004, at 00:06, zuzu wrote: > > >> string.puts(STDOUT) creates a coupling, the string must call a method > >> of the > >> given object with itself as the argument. It must know that the > >> object it's > >> passed responds to #puts. > > > > i think to me this seems natural for the pure-OO "everything is an > > object" nature of ruby (or smalltalk). strings know how to display > > themselves, they just need to know where. again, i think "naked > > objects" is the latest way to talk about this, though i often think of > > self language which was built on smalltalk. > > Regarding the "string displaying itself"-bit, my view is that a string > can suggest how it should be displayed, but the final call is in the > hands of the display object. > As dataflow: cat string | /dev/display yes yes, i agree. i came to the same conclusion some time ago discussing the ION windowmanager with tuomo valkonen. http://modeemi.cs.tut.fi/~tuomov/ion/#intro (windowmanagers conforming to the ICCCM, not applications themselves, should decide how they are drawn. but the applications convey to the windowmanager *what* needs to be drawn.) the naked objects book is online at http://www.nakedobjects.org/book/content.html (though i personally favor a GUI much more like ION / screen / emacs than the popular WIMP design.) > >> The difference to Ruby's method.method.method is that the shell pipes > >> aren't > >> namespaced to the preceding object. Ie. With shell pipes it'd be > >> quite doable > >> to except the following to work in every situation: > >> > >> gets | reverse | puts , because they are in the global namespace. > >> What you pass > >> through the pipe doesn't change the available methods. > >> > >> In ruby > >> > >> gets.reverse.puts > >> creates a dependency chain. > >> > >> gets must return something that responds to reverse, and reverse must > >> return > >> something that responds to puts. Which is bad. > > > > hmmm... is this any different than unix which works blindly in > > bytestreams? > > The shell pipes differ in that shell commands are all "instance methods > of class Bytestream", but in Ruby the input object and output object > may be of different classes. So there's a possibility of interface > mismatch. ah, yes. hence the .to_x adapter methods (popularly, .to_s). this may be the ruby way, but something is scratching at the back of my subconscious that i've seen something regarding language design about how this can possibly be inferred by the interpreter or that there's an elegant design way around it... if/when i think of this hopefully i'll remember to post... (something related to duck-typing maybe???) > With shell commands, the methods all have the same input and output > type, but they are liable to fail if the incoming stuff doesn't quite > agree with what the command was built to handle. > > cat my.wav | lame - - | madplay - > cat my.txt | lame - - | madplay - > # if my.txt happens to be a valid wav file, it works > # if not, lame blows up. right, this was what i was trying to allude to before w/r/t bytestreams. though again, if there's an elegant way for the code to know when something won't connect properly, that's better for the programmer / user i think. > Compared to: > Wav.new('my.wav').lame.madplay # works, may fail inside call to lame > if input is bad > Txt.new('my.txt').lame.madplay #=> NoMethodError, even if my.txt is > a valid wav file right right, useful feedback is very important. i'm all about introspection/reflection; i <3 irb. > The way shell commands work could be rubyized as: > > require 'thread' i need to look over this code in more detail, but as an aside i would like to avoid ruby-threads (and threads in general) as a matter of the problems with shared-state-concurrency. a longer-term goal i am seeking is to extend ruby from pure-OO to pure-actors (as in carl hewitt's actors model, 'concepts techniques and models of computer programming' elaborates on this where the SICP does not). essentially, every object instance would be its own actor, though this may require writing an alternate VM for ruby which would utilize what fare@tunes.org refers to as "no kernel" architecture ( http://cliki.tunes.org/No-Kernel ); though compatability would be maintained by aliasing any Kernel object requests back to the Object object (sort of the inverse of the current mix-in implementation). jim's method conveniently side-steps the issue on the ruby-code side of things by using Enumerable instead. though this may not be the "perfect" solution either. i hope to spend more time starting this week to try some experimentation with these models in actual code. i think somewhere someone also mentioned that in common programming there is often a muddeling between design and platform implementation, or something regarding optimization.... (i don't think it was the TAoUP... was it?) but the gist was that the language should aid design, and the interpreter (VM, compiler, whatever...) should already know and automate the optimal solutions to problems like "should this be threaded?" a common example today, though oddly still hotly debated, is manual memory management versus garbage collection (GC). according to this "sage advice" i am trying to recall here, language should not offer manual memory management (i hear all the C/asm/"efficiency" nazis crawling out of the woodwork as i type this...) because it is precisely the kind of task which the computer will *consistently* do better than a human. yes, once in a blue moon a human can exert an excessive effort to outsmart a GC, but doing so and not creating buggy code requires a super-human programmer to both create _and maintain_; to quote from the TAoUP however, "computer time is cheap. human time is expensive." my point is, any ruby code that actually sticks around to implement something like late-binding / lazy-evaluation streams / dataflow should rely on the transparency of the interpreter / VM to decide the optimal utilization of the underlying hardware, and hopefully, as john backus desired, free us from the constraints of the von neumann machine. (however, ilmari, i realize your code was merely some brainstorming. popular brainstorming of designs in code is the pursued ideal here afterall.) peace, -z > class ShellPipe > # create method for each command in each path with > # paths in front overriding later ones > # quite obtuse. > ENV['PATH'].split(/:/).reverse.each{|path_root| > Dir[File.join(path_root, "*")].each{|bin_name| > define_method(File.split(bin_name).last){|*args| > full_cmd = [bin_name, *args].join(" ") > self.class.new( > self, > IO.popen(full_cmd, 'r+') > ) > } > } > } > > def initialize(input, filter, stopped=false) > @input = input > @filter = filter > @stopped = stopped > @mutex = Mutex.new > @cond = ConditionVariable.new > suck_on_input > end > > def silently_fail > yield > rescue > end > > def method_missing(m, *args, &block) > @filter.send(m, *args, &block) > rescue NoMethodError > super > end > > def suck_on_input > silently_fail{ @input_sucker.kill } > @input_sucker = Thread.new{ > begin > @mutex.synchronize{ > until @input.eof? > @cond.wait(@mutex) if @stopped > @filter.write @input.read(4096) > end > } > ensure > @filter.close_write > end > } > end > > def start > return nil unless @stopped > @mutex.synchronize{ > @stopped = false > @cond.signal > } > end > > def stop > @stopped = true > end > > def read(*args) > @filter.read(*args) > end > > def close > @input.close if @input and not @input.closed? > ensure > @filter.close > end > end > > my_wav = ShellPipe.new(nil, File.open("my.wav")) > player = my_wav.lame("-", "-").madplay("-") > player.read > player.close # cascades up the pipe > > Got a little carried away there, sorry about that :) > >