Open LeFreq opened 10 years ago
to pass data in and out of objects
Can you explain what you mean here? That sounds pretty generic, which isn't exactly clarifying.
It is "generic" (so to speak). That's the idea. Instead of hundreds of different method invocations passing data in and out, you have ONE standardized way to do it -- and it forces a mindset on the programmers to consider that their objects are for coupling with one another and to design them that way from the start.
Example: msg >> NetworkPort #send a data packet to the network myString >> ch #get a character from a string.
In general, a convention should be made to avoid the symmetry of the operator symbols making a redundant form. Despite the popular idea of "everything should be an object", it would be better to note the one-directional distinction between data and objects, ending an otherwise infinite regress at the bit.
So in the above examples, I am sending a piece of data to an object in the first, but getting a piece of data in the second, even though I'm using the same symbol. The operator would bind to the most privileged Object, in other words to the one most likely to do the "right thing".
I don’t really understand what @theProphet means, but here is a guess. Maybe this guess can get the conversation moving the right direction.
Methods would be forbidden from initializing fields on an object or returning their values; they can only modify fields. Outside an object, code can initialize and read fields explicitly using two standard methods like <<
and >>
. So instead of this (Ruby):
class Counter
def initialize
@count = 0
end
def bump
@count += 1
end
# (the long form of `attr_reader :count`)
def count
@count
end
end
counter = Counter.new
counter.bump
final_count = counter.count
You would have to do this, making the data passing explicit:
class Counter
stores :count
def bump
@count += 1
end
end
counter = Counter.new <<(count: 0)
counter.bump
final_count = counter >>(:count)
I don’t see this as an improvement in this case. It seems like it encourages immutable objects so that you only have to call <<
once, when you create the object, but there are probably better ways to encourage immutable objects that don’t force you to know so much about the internal state. But maybe that just means Counter
is a bad example of people’s “little forests of their own personal domain languages – an ontology of objects of which only they really relate”. I would be interested to see a better example, that is obviously messy, and that could be improved if the objects were forced to use generic data access methods.
No. That's actually a different design idea that is somewhat interesting. I'd have to evaluate that further.
But, no, the idea of message-passing involves making semi-private classes that have to be interacted with pre-conceived input and output mechanisms. You're trying to insure both the safety of the class and encourage the class designers to think of their classes as members of a large, dynamic ecosystem.
The concept is akin to requiring you to speak to my ears in order to exert influence, rather than using a scapal (the C way), pills (functors), or having to capitulate to my family language (high-German) to communicate to me.
A standard-message passing syntax makes a universal language for all objects (something like Esperanto) and makes all language designers really think about why they're creating objects that don't communicate to the environment or why they expect hundreds of users to learn their special hyper-personal API.
In closing, your Counter class is a rather bad example, because having such a class implies exposing a meaningless internal artifact to the user in the data ecosystem. Such a thing should be within a hierarchy of class enclosures, manipulated by a master class. But, staying with your example, the usage would be more like this:
myCounter = Counter.new myCounter << ScreenButton && MouseDown
Let me see if I understand you. In your example, you are connecting the input of myCounter
to the output of the compound (decorated?) object ScreenButton && MouseDown
. The counter will be bumped when a certain on-screen button is clicked.
So you want a system where manually naming or calling a bump
method is unnecessary; input just goes to the object’s “standard input”. And maybe there would be a limited number of standard hooks for each object – like an object’s standard input, standard output, and standard error output. Maybe there would also be one pair of secondary input and output.
When you say you don’t want objects that “expect hundreds of users to learn their special hyper-personal API”, you mean that a Counter
doesn’t have a uniquely-named bump
method; it just has standard input, so that users of the Counter
API don’t have to remember the name of the method that increments the counter.
When I try to imagine a system that follows these rules, I come up with one where people would convert most method names into class names, by splitting behavior into lots of different objects. For example, a String
might normally have public internal iterator methods like each_char
, each_codepoint
, and each_line
. That is too many variations to represent through some standard API. So instead of a_string.each_line { |line| print line }
, you might do String::Iterators::Line(a_string) >> { |line| print line }
. In that example, the iterator object outputs each item through “standard output” to an anonymous function.
Let me think of another example, a Date
class. You should be able to read the various fields of a date (year, month, day), as well as get a new Date
that is some number of days ahead or behind, and also get the number of days difference between two Date
s. With these rules, we can no longer read year, month, and day values through accessor methods like year
. Perhaps instead, standard output my_date >> a_record_object
would result in the new variable a_record_object
containing a hash-map like {year: 2014, month: 9, day: 12}
. To read a_record_object
, maybe you give the key as standard input with a_record_object << :year
and read standard output with a_record_output >> the_year
to get the value. To add days to a Date
, I suppose you have to use a separate object again: Date::DayAdder(my_date) << 3 >> date_three_days_later
(using convenient syntax to both input and output on the same line). And to get the difference, another separate object: Date::DifferenceTaker(my_date, another_date) >> num_days_difference
.
I also thought of another interpretation. Maybe I am taking your idea too far, and rather than outlawing all non-standard method names, you are simply suggesting that there be standard method names for just the act of input to and output from an object, while other methods could still have custom names. In that case, Date
might have the >>
syntax to read a hash-map of values, but unusual behavior like adding days and taking differences would be normal my_date.plus_days(3)
method calls. Or you might define those two methods to use the operators +
and -
, which could also be standard method names – but there is no such shortcut for the String
example, where you would still have three different method names like each_line
.
Do either of these reflect the system you are thinking of? If not, how might you represent these String
iterator and Date
examples?
This is called "concatenative programming", and is a really good idea (think forth + infix)
class Counter
def init
@count = 0
end
attr :count
end
c = Counter.new
ButtonPressed and KeyPressed >> c.bump
>>
would work like unix |
.
In other words, >>
and <<
would work as composition operators:
f1(f2(x)) == x >> f2 >> f1 == f1 << f2 << x == f1 << (x >> f2) # etc
Is this what you mean? (ruby)
class Object
def >> other
other.call self
end
def << other
call other
end
end
If I have understood magpie's philosophy correctly, I would like to talk to you about standardizing on a message-passing syntax that would take OOP languages to a new level. The OOP field has been in disarray for awhile, most have accomidated it because no one really knows how to take the programming art beyond application programming or functional calculus.
Specifically, if you use a standard operator syntax (like C++'s >> and << I/O operators) to pass data in and out of objects, you will avoid the common tendency for people to create little forests of their own personal domain languages -- a taxonomy of objects of which only they really relate. Passing data in and between objects is a common theme in OOP, and no procedural language, to my knowledge, has made a standard, specialized syntax for it (beyond parameter passing).