On Sun, 12 Dec 2004 09:03:54 +0900, Ilmari Heikkinen <kig / misfiring.net> 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 :)
> 
>