How do Smalltalk disambiguate messages?
I am trying to do a JavaScript experiment to make Objects behave like Smalltalk objects. example: "reminder: Smalltalk arrays start at 1." |arr| a := Array new: 1. a at: 1 put: 'Hello, World'. Here, the Smalltalk array has a message "at: index put: value" which is defined many layers of prototype chaining down, at Object in category accessing. However there are many messages on the way traversing the prototype chain that start with "at:", such as(SequenceableCollection) "at: x ifAbsent: y", "at: x incrementBy: y". How does Smalltalk tell which message is which? Does it merge the messages into a single message such as "at: x put: y" to "atPut(x, y)" at some point? Without knowing this, I have to traverse the whole call chain and check whether each argument is called in a specific way, which is incredibly expensive. If there was a single name such as "atPut" I can easily just do a dictionary lookup for this name and get it at O(1). Does Smalltalk actually store each message? -- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
The message is "at:put:". It's a single message send. I encourage you to try the ProfStef tutorial to understand the basis. On Oct 16, 2016 01:17, "CodeDmitry" <dimamakhnin@gmail.com> wrote:
I am trying to do a JavaScript experiment to make Objects behave like Smalltalk objects.
example:
"reminder: Smalltalk arrays start at 1." |arr| a := Array new: 1. a at: 1 put: 'Hello, World'.
Here, the Smalltalk array has a message "at: index put: value" which is defined many layers of prototype chaining down, at Object in category accessing.
However there are many messages on the way traversing the prototype chain that start with "at:", such as(SequenceableCollection) "at: x ifAbsent: y", "at: x incrementBy: y".
How does Smalltalk tell which message is which? Does it merge the messages into a single message such as "at: x put: y" to "atPut(x, y)" at some point?
Without knowing this, I have to traverse the whole call chain and check whether each argument is called in a specific way, which is incredibly expensive. If there was a single name such as "atPut" I can easily just do a dictionary lookup for this name and get it at O(1).
Does Smalltalk actually store each message?
-- View this message in context: http://forum.world.st/How-do- Smalltalk-disambiguate-messages-tp4918946.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
I understand that it is a single message send, but to know how to handle the message at runtime, the parser needs to somehow determine where the implementation of that message is. It must do a lookup based on multiple keys(at, and put), which is really confusing. "methods" are easy to look up because they only have one name, but messages have one name which is split amongst each argument. Simple concatenation does not make sense, since {at: x, put: y} and {a: x, tput: y} are different messages. The only way I can think of this being implemented "easily" is by storing a table of entries {num_args, arg_names, impl_ref}, and then have the function "send" do a linear search over this table until it finds an entry with num_args = 2, each arg_name matching the argument name keys in order, and then calling the impl_ref with the array of values without the names. I'm wondering if there is a way to do this without having to loop over every single message the object has, then every message its' parent object has, and so on, just to find which function you actually want to call. -- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
I think the nub of your confusion is twofold. The method #at:put: is a keyword method and it isn't right to think o them as two keys. It is one compound key and unlike most every other system, Smalltalk keyword message sends allow the programmer to name each argument. This is very fine indeed. The other item is in your thinking that these methods are defined in a prototype chain, like JavaScript. Primarily Smalltalk is a class-based inheritance system for method lookup on message send. What I mean by your example is that when the message #at:put: is sent, the system will start with the concrete class in question, Array, and attempts lookup of #at:put:. When this fails, it then traverses the lookup to the superclass (->ArrayedCollection) and so on on lookup failure (->SequenceableCollection ->Collection ->Object) and at the Object class, the lookup into that Class's methodDictionary succeeds. It tuns out that Object>>#at:put: is a primitive in the vm (#61) that is able to store into an Array object, a variable class and indexable, by the way. Consider the method Array>>atWrap:put: also calling primitive 61. This may help with foundations: http://samizdat.cc/shelf/documents/2004/08.02-historyOfSmalltalk/historyOfSm... I hope this helps. On 10/16/2016 12:44 AM, CodeDmitry wrote:
I understand that it is a single message send, but to know how to handle the message at runtime, the parser needs to somehow determine where the implementation of that message is. It must do a lookup based on multiple keys(at, and put), which is really confusing. "methods" are easy to look up because they only have one name, but messages have one name which is split amongst each argument.
Simple concatenation does not make sense, since {at: x, put: y} and {a: x, tput: y} are different messages.
The only way I can think of this being implemented "easily" is by storing a table of entries {num_args, arg_names, impl_ref}, and then have the function "send" do a linear search over this table until it finds an entry with num_args = 2, each arg_name matching the argument name keys in order, and then calling the impl_ref with the array of values without the names.
I'm wondering if there is a way to do this without having to loop over every single message the object has, then every message its' parent object has, and so on, just to find which function you actually want to call.
-- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
Hi, : is part of the method name. So, the method: Dictionary>>at: key put: anObject has the name #'at:put:' and the object defining this method will understand a message #âat:put:â. To better understand this, you can play with inspecting the method objects, like below: Does this answer the question? Cheers, Doru
On Oct 16, 2016, at 6:44 AM, CodeDmitry <dimamakhnin@gmail.com> wrote:
I understand that it is a single message send, but to know how to handle the message at runtime, the parser needs to somehow determine where the implementation of that message is. It must do a lookup based on multiple keys(at, and put), which is really confusing. "methods" are easy to look up because they only have one name, but messages have one name which is split amongst each argument.
Simple concatenation does not make sense, since {at: x, put: y} and {a: x, tput: y} are different messages.
The only way I can think of this being implemented "easily" is by storing a table of entries {num_args, arg_names, impl_ref}, and then have the function "send" do a linear search over this table until it finds an entry with num_args = 2, each arg_name matching the argument name keys in order, and then calling the impl_ref with the array of values without the names.
I'm wondering if there is a way to do this without having to loop over every single message the object has, then every message its' parent object has, and so on, just to find which function you actually want to call.
-- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
-- www.tudorgirba.com www.feenk.com "We can create beautiful models in a vacuum. But, to get them effective we have to deal with the inconvenience of reality."
So "at: x put: y" translates to a method named #at:put:(or "at:put:")? |dict| dict := Dictionary new. dict at: 'foo' put: 'bar'. So javascript equivalent would be(assuming the Dictionary object existed). var dict; dict = new Dictionary; dict['at:put:']('foo', 'bar'); -- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
The value of the method name is at:put: # denotes a Symbol, which is a subclass of String. The difference between the two is that two symbols with the same value will actually be the same object, while this is not necessarily the case with strings: stringA := String newFrom: 'string'. stringB := String newFrom: 'string'. stringA == stringB âfalse". symbolA := Symbol newFrom: 'symbol'. symbolB := Symbol newFrom: 'symbol'. symbolA == symbolB "true" Cheers, Doru
On Oct 16, 2016, at 7:19 AM, CodeDmitry <dimamakhnin@gmail.com> wrote:
So "at: x put: y" translates to a method named #at:put:(or "at:put:")?
|dict| dict := Dictionary new. dict at: 'foo' put: 'bar'.
So javascript equivalent would be(assuming the Dictionary object existed).
var dict;
dict = new Dictionary; dict['at:put:']('foo', 'bar');
-- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
-- www.tudorgirba.com www.feenk.com "The coherence of a trip is given by the clearness of the goal."
`There is no equivalent in JavaScript because Javascript does not have function calls with keyword arguments and even if it , it would still be not equivalent at least technically. The message "at:put:" is indeed a keyword that is part of the dictionary of the object and not just a generic dictionary. So each object has its own dictionary that associates keywords to method calls. So in Smalltalk you can not access neither variables (class or instance) nor methods (class or instance) outside the object, they all are accessed through messages. This happens to hide the implementation, hence the complexity of the object. The user does not need to know how the Object works only how to use it. The only other languages that have such an implementation are ObjC and Ruby which borrow this directly from Smalltalk. Also Javascript uses prototype based OOP while Snalltalk is the first language to implement class based OOP. They are similar but not the same. On Sun, Oct 16, 2016 at 8:28 AM CodeDmitry <dimamakhnin@gmail.com> wrote:
So "at: x put: y" translates to a method named #at:put:(or "at:put:")?
|dict| dict := Dictionary new. dict at: 'foo' put: 'bar'.
So javascript equivalent would be(assuming the Dictionary object existed).
var dict;
dict = new Dictionary; dict['at:put:']('foo', 'bar');
-- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
I was actually curious about this in Ruby as well, since Ruby also doesn't have the Smalltalk message syntax. I figure that the magic behind it is that Smalltalk takes strings like "dict at: 'foo' put: 'bar'" and evaluates them into a JavaScript equivalent of "dict['at:put:']('foo', 'bar')". Basically, my proof of concept(I cheated with regex to cut time): 'use strict'; var Dictionary = function Dictionary() { this.dict = {}; }; Dictionary.prototype['at:put:'] = function(index, value) { this.dict[index] = value; }; Object.defineProperty(Dictionary, 'new', { get: function () { return new this; } }); function st_eval(str) { var x = str.match(/(dict)\s+(at):\s+'(foo)'\s+(put):\s+'(bar)'/); var target = x[1]; var callName = x[2] + ':' + x[4] + ':'; var argv = [x[3], x[5]]; var realTarget = eval(target); realTarget[callName].apply(realTarget, argv); } var dict = Dictionary.new; console.log(dict); //dict['at:put:']('foo', 'bar'); console.log(dict); st_eval("dict at: 'foo' put: 'bar'"); console.log(dict); -- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
"I was actually curious about this in Ruby as well, since Ruby also doesn't have the Smalltalk message syntax. " Actually, it does when i said in my reply "The only other languages that have such an implementation are ObjC and Ruby which borrow this directly from Smalltalk." What I mean is that both ObjC and Ruby has a message based OOP system , like Smalltalk. http://rubylearning.com/blog/2010/11/03/do-you-understand-rubys-objects-mess... On Sun, Oct 16, 2016 at 11:50 AM CodeDmitry <dimamakhnin@gmail.com> wrote:
I was actually curious about this in Ruby as well, since Ruby also doesn't have the Smalltalk message syntax.
I figure that the magic behind it is that Smalltalk takes strings like "dict at: 'foo' put: 'bar'" and evaluates them into a JavaScript equivalent of "dict['at:put:']('foo', 'bar')".
Basically, my proof of concept(I cheated with regex to cut time):
'use strict';
var Dictionary = function Dictionary() { this.dict = {}; }; Dictionary.prototype['at:put:'] = function(index, value) { this.dict[index] = value; }; Object.defineProperty(Dictionary, 'new', { get: function () { return new this; } });
function st_eval(str) { var x = str.match(/(dict)\s+(at):\s+'(foo)'\s+(put):\s+'(bar)'/); var target = x[1]; var callName = x[2] + ':' + x[4] + ':'; var argv = [x[3], x[5]]; var realTarget = eval(target); realTarget[callName].apply(realTarget, argv); }
var dict = Dictionary.new; console.log(dict); //dict['at:put:']('foo', 'bar'); console.log(dict); st_eval("dict at: 'foo' put: 'bar'"); console.log(dict);
-- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
I tried so hard to find an example of Ruby using smalltalk message syntax. You can do something like: class Dictionary attr_accessor :dict def initialize() @dict = {} end def at(*args) if (args.length == 1) then return @dict[args[0]] end @dict[args[0]] = args[1][:put] end end dict = Dictionary.new dict.at 3, put: 4 puts dict.dict puts dict.at 3 But it's pretty expensive to disambiguate messages to "at" this way. The JS approach was much simpler since each message like 'at:put:' or 'at' were sent to appropriate receiver functions, whereas this is just a spaghetti :\ I'm sure there's more elegant way of doing this in Ruby but I haven't found it :| -- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
Still you don't understand how Ruby syntax works. Every method call every function you see is basically a masquerade message sent. Those calls are just syntactic sugar, they don't exist, they are there to make people to feel more comfortable giving them the illusion that is a call because it looks like a call, but is all message sents. So there is no need to do what you doing . Ruby does not just implement message scents, it's basically Smalltalk pretending to be something else. So most of Smalltalk syntax is there. For me it's is the ONLY language that can claim it's Smalltalk language inspired. Messages are very hard to implement if not impossible because they required that calls don't exist or be disabled, otherwise the existence of message is pointless. And since no language I am aware of allow you to disable calls and replace them with messages , what you trying to do is pointless . Of course to be devil's advocate here Smalltalk in vast majority uses messages as calls so there existence is kinda pointless too, risking to be burn in fire by my fellow Smalltalkers , I have only seen the power of message to be taken advantage of in the case of messageNotUnderstood be linked to doesNotUnderstand method. In every other case I have encountered the message always has the same name as the method, which defeats the purpose of the message in the first place. This is also the default behavior of the IDE. On Sun, 16 Oct 2016 at 12:54, CodeDmitry <dimamakhnin@gmail.com> wrote:
I tried so hard to find an example of Ruby using smalltalk message syntax. You can do something like:
class Dictionary attr_accessor :dict
def initialize() @dict = {} end
def at(*args) if (args.length == 1) then return @dict[args[0]] end @dict[args[0]] = args[1][:put] end end
dict = Dictionary.new dict.at 3, put: 4 puts dict.dict puts dict.at 3
But it's pretty expensive to disambiguate messages to "at" this way.
The JS approach was much simpler since each message like 'at:put:' or 'at' were sent to appropriate receiver functions, whereas this is just a spaghetti :\
I'm sure there's more elegant way of doing this in Ruby but I haven't found it :|
-- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
"Messages are very hard to implement if not impossible because they required that calls don't exist or be disabled, otherwise the existence of message is pointless. And since no language I am aware of allow you to disable calls and replace them with messages , what you trying to do is pointless ." Can you explain in more detail what you mean by this? I am not sure how other languages cannot do this. -- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
Hi,
On Oct 16, 2016, at 10:41 AM, CodeDmitry <dimamakhnin@gmail.com> wrote:
I was actually curious about this in Ruby as well, since Ruby also doesn't have the Smalltalk message syntax.
I figure that the magic behind it is that Smalltalk takes strings like "dict at: 'foo' put: 'bar'" and evaluates them into a JavaScript equivalent of "dict['at:put:']('foo', 'bar')â.
There is no magic. This is how the syntax is. Instead of parentheses that accumulate all parameters in a single place like: x.atput(a,b); we can insert the argument inside the message name, like: x at: a put: b. Itâs not magic, is just different. Thatâs all. Except that one makes it possible to write code that resembles natural language. Doru
Basically, my proof of concept(I cheated with regex to cut time):
'use strict';
var Dictionary = function Dictionary() { this.dict = {}; }; Dictionary.prototype['at:put:'] = function(index, value) { this.dict[index] = value; }; Object.defineProperty(Dictionary, 'new', { get: function () { return new this; } });
function st_eval(str) { var x = str.match(/(dict)\s+(at):\s+'(foo)'\s+(put):\s+'(bar)'/); var target = x[1]; var callName = x[2] + ':' + x[4] + ':'; var argv = [x[3], x[5]]; var realTarget = eval(target); realTarget[callName].apply(realTarget, argv); }
var dict = Dictionary.new; console.log(dict); //dict['at:put:']('foo', 'bar'); console.log(dict); st_eval("dict at: 'foo' put: 'bar'"); console.log(dict);
-- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
-- www.tudorgirba.com www.feenk.com âThe smaller and more pervasive the hardware becomes, the more physical the software gets."
I define Magic as "An opaque abstraction or an abstraction you think is opaque until you learn better.", to a beginner, everything is deeply Magical. That said, much of Smalltalk's opaqueness is not due to the language, but due to me being a beginner. I'm sure there's a way to actually force sending a message to a dictionary via something like dict['at:put:']('foo', 'bar') but I am not familiar enough with Smalltalk to express it, so it feels like the only way to send a message is via this multi-part message abstraction. -- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
Am 16.10.2016 14:35 schrieb "CodeDmitry" <dimamakhnin@gmail.com>:
I define Magic as "An opaque abstraction or an abstraction you think is opaque until you learn better.", to a beginner, everything is deeply Magical.
That said, much of Smalltalk's opaqueness is not due to the language, but due to me being a beginner. I'm sure there's a way to actually force
sending
a message to a dictionary via something like dict['at:put:']('foo', 'bar') but I am not familiar enough with Smalltalk to express it, so it feels like the only way to send a message is via this multi-part message abstraction.
You can always access the method dict directly to get a method and call "perform" or perform with arguments.
-- View this message in context:
http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189...
Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
On Sun, Oct 16, 2016 at 8:38 PM, Nicolai Hess <nicolaihess@gmail.com> wrote:
Am 16.10.2016 14:35 schrieb "CodeDmitry" <dimamakhnin@gmail.com>:
I define Magic as "An opaque abstraction or an abstraction you think is opaque until you learn better.", to a beginner, everything is deeply Magical.
That said, much of Smalltalk's opaqueness is not due to the language, but due to me being a beginner. I'm sure there's a way to actually force sending a message to a dictionary via something like dict['at:put:']('foo', 'bar') but I am not familiar enough with Smalltalk to express it, so it feels like the only way to send a message is via this multi-part message abstraction.
You can always access the method dict directly to get a method and call "perform" or perform with arguments.
To put that in more concrete terms... a := Array new: 1. a perform: #at:put: withArguments: {1. 'Hello, world'}. a inspect. Note how #at:put: is a single identifier. cheers -ben
Thanks! That works! (And I learned that I can create collections with {a. b. c} syntax). -- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
Smalltalk does not allow you to do calls of any kind, everything goes through the messaging system . To have the same in another language you will have to disable the ability to call functions and method and replace them with messages. No language such feature not even lisp, the language that excels at manipulating its syntax . In order to implement this you will have to go deep into the implementation of the language and essentially change it to something , probably a new programming language. On Sun, 16 Oct 2016 at 15:35, CodeDmitry <dimamakhnin@gmail.com> wrote:
I define Magic as "An opaque abstraction or an abstraction you think is opaque until you learn better.", to a beginner, everything is deeply Magical.
That said, much of Smalltalk's opaqueness is not due to the language, but due to me being a beginner. I'm sure there's a way to actually force sending a message to a dictionary via something like dict['at:put:']('foo', 'bar') but I am not familiar enough with Smalltalk to express it, so it feels like the only way to send a message is via this multi-part message abstraction.
-- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
Here, Dmitry, try this code in playground...maybe helps you understand | lookupClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('selector lookup failure: ', selector)]. (cls methodDictionary at: selector ifAbsent: [nil]) ifNil: [lookupClosure value: cls superclass value: selector] ifNotNil: [:meth | Smalltalk tools browser openOnMethod: meth]]. lookupClosure value: Array value: #at:put:. On 10/16/2016 8:27 AM, CodeDmitry wrote:
I define Magic as "An opaque abstraction or an abstraction you think is opaque until you learn better.", to a beginner, everything is deeply Magical.
That said, much of Smalltalk's opaqueness is not due to the language, but due to me being a beginner. I'm sure there's a way to actually force sending a message to a dictionary via something like dict['at:put:']('foo', 'bar') but I am not familiar enough with Smalltalk to express it, so it feels like the only way to send a message is via this multi-part message abstraction.
-- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
On Sun, Oct 16, 2016 at 9:11 PM, Charlie Robbats <charlie.robbats@gmail.com> wrote:
Here, Dmitry, try this code in playground...maybe helps you understand
| lookupClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('selector lookup failure: ', selector)]. (cls methodDictionary at: selector ifAbsent: [nil]) ifNil: [lookupClosure value: cls superclass value: selector] ifNotNil: [:meth | Smalltalk tools browser openOnMethod: meth]]. lookupClosure value: Array value: #at:put:.
Nice example. Slightly improved... | lookupClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('selector lookup failure: ', selector)]. cls methodDictionary at: selector ifPresent: [:meth | Smalltalk tools browser openOnMethod: meth] ifAbsent: [lookupClosure value: cls superclass value: selector]]. lookupClosure value: Array value: #at:put:. cheers -ben
Thanks, that's a good improvement, more readable. On 10/16/2016 9:32 AM, Ben Coman wrote:
On Sun, Oct 16, 2016 at 9:11 PM, Charlie Robbats <charlie.robbats@gmail.com> wrote:
Here, Dmitry, try this code in playground...maybe helps you understand
| lookupClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('selector lookup failure: ', selector)]. (cls methodDictionary at: selector ifAbsent: [nil]) ifNil: [lookupClosure value: cls superclass value: selector] ifNotNil: [:meth | Smalltalk tools browser openOnMethod: meth]]. lookupClosure value: Array value: #at:put:. Nice example. Slightly improved...
| lookupClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('selector lookup failure: ', selector)]. cls methodDictionary at: selector ifPresent: [:meth | Smalltalk tools browser openOnMethod: meth] ifAbsent: [lookupClosure value: cls superclass value: selector]]. lookupClosure value: Array value: #at:put:.
cheers -ben
Here's a little change to get your #perform:withArguments: implemented. | array lookupClosure method | array := { 1 }. lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('method lookup failure: ', selector)]. cls methodDictionary at: selector " ifPresent: [:meth | Smalltalk tools browser openOnMethod: meth]" ifPresent: [:meth | meth] ifAbsent: [lookupClosure value: cls superclass value: selector]]. method := lookupClosure value: Array value: #at:put:. method valueWithReceiver: array arguments: {1. 'Hello, world'}. array On 10/16/2016 9:32 AM, Ben Coman wrote:
On Sun, Oct 16, 2016 at 9:11 PM, Charlie Robbats <charlie.robbats@gmail.com> wrote:
Here, Dmitry, try this code in playground...maybe helps you understand
| lookupClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('selector lookup failure: ', selector)]. (cls methodDictionary at: selector ifAbsent: [nil]) ifNil: [lookupClosure value: cls superclass value: selector] ifNotNil: [:meth | Smalltalk tools browser openOnMethod: meth]]. lookupClosure value: Array value: #at:put:. Nice example. Slightly improved...
| lookupClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('selector lookup failure: ', selector)]. cls methodDictionary at: selector ifPresent: [:meth | Smalltalk tools browser openOnMethod: meth] ifAbsent: [lookupClosure value: cls superclass value: selector]]. lookupClosure value: Array value: #at:put:.
cheers -ben
and a little block cleanup: | array lookupClosure performClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('method lookup failure: ', selector)]. cls methodDictionary at: selector ifPresent: [:meth | meth] ifAbsent: [lookupClosure value: cls superclass value: selector]]. performClosure := [ :receiver :args | (lookupClosure value: array class value: #at:put:) valueWithReceiver: array arguments: args ]. array := { 1 }. performClosure value: array value: {1. 'Hello, world'}. array inspect. Charlie On 10/16/2016 9:54 AM, Charlie Robbats wrote:
Here's a little change to get your #perform:withArguments: implemented.
| array lookupClosure method | array := { 1 }. lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('method lookup failure: ', selector)]. cls methodDictionary at: selector " ifPresent: [:meth | Smalltalk tools browser openOnMethod: meth]" ifPresent: [:meth | meth] ifAbsent: [lookupClosure value: cls superclass value: selector]]. method := lookupClosure value: Array value: #at:put:. method valueWithReceiver: array arguments: {1. 'Hello, world'}. array
On 10/16/2016 9:32 AM, Ben Coman wrote:
On Sun, Oct 16, 2016 at 9:11 PM, Charlie Robbats <charlie.robbats@gmail.com> wrote:
Here, Dmitry, try this code in playground...maybe helps you understand
| lookupClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('selector lookup failure: ', selector)]. (cls methodDictionary at: selector ifAbsent: [nil]) ifNil: [lookupClosure value: cls superclass value: selector] ifNotNil: [:meth | Smalltalk tools browser openOnMethod: meth]]. lookupClosure value: Array value: #at:put:. Nice example. Slightly improved...
| lookupClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('selector lookup failure: ', selector)]. cls methodDictionary at: selector ifPresent: [:meth | Smalltalk tools browser openOnMethod: meth] ifAbsent: [lookupClosure value: cls superclass value: selector]]. lookupClosure value: Array value: #at:put:.
cheers -ben
Oops, that should be.. | array lookupClosure performClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('method lookup failure: ', selector)]. cls methodDictionary at: selector ifPresent: [:meth | meth] ifAbsent: [lookupClosure value: cls superclass value: selector]]. performClosure := [ :receiver :selector :args | (lookupClosure value: receiver class value: selector) valueWithReceiver: receiver arguments: args ]. array := { 1 }. performClosure value: array value: #at:put: value: {1. 'Hello, world'}. array inspect. On 10/16/2016 10:03 AM, Charlie Robbats wrote:
and a little block cleanup:
| array lookupClosure performClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('method lookup failure: ', selector)]. cls methodDictionary at: selector ifPresent: [:meth | meth] ifAbsent: [lookupClosure value: cls superclass value: selector]]. performClosure := [ :receiver :args | (lookupClosure value: array class value: #at:put:) valueWithReceiver: array arguments: args ]. array := { 1 }. performClosure value: array value: {1. 'Hello, world'}. array inspect.
Charlie
On 10/16/2016 9:54 AM, Charlie Robbats wrote:
Here's a little change to get your #perform:withArguments: implemented.
| array lookupClosure method | array := { 1 }. lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('method lookup failure: ', selector)]. cls methodDictionary at: selector " ifPresent: [:meth | Smalltalk tools browser openOnMethod: meth]" ifPresent: [:meth | meth] ifAbsent: [lookupClosure value: cls superclass value: selector]]. method := lookupClosure value: Array value: #at:put:. method valueWithReceiver: array arguments: {1. 'Hello, world'}. array
On 10/16/2016 9:32 AM, Ben Coman wrote:
On Sun, Oct 16, 2016 at 9:11 PM, Charlie Robbats <charlie.robbats@gmail.com> wrote:
Here, Dmitry, try this code in playground...maybe helps you understand
| lookupClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('selector lookup failure: ', selector)]. (cls methodDictionary at: selector ifAbsent: [nil]) ifNil: [lookupClosure value: cls superclass value: selector] ifNotNil: [:meth | Smalltalk tools browser openOnMethod: meth]]. lookupClosure value: Array value: #at:put:. Nice example. Slightly improved...
| lookupClosure | lookupClosure := []. lookupClosure := [:cls :selector | (cls == nil) ifTrue: [Warning signal: ('selector lookup failure: ', selector)]. cls methodDictionary at: selector ifPresent: [:meth | Smalltalk tools browser openOnMethod: meth] ifAbsent: [lookupClosure value: cls superclass value: selector]]. lookupClosure value: Array value: #at:put:.
cheers -ben
For an example of a working bridge between the two ways of methods and function see PyObjc which allows Python code to call Objective C messaging (e.g in a similar form to your mapping Javascript to Smalltalk - except Javascript is not class based) https://pythonhosted.org/pyobjc/core/intro.html Mark On 16/10/2016 12:59, Tudor Girba wrote:
Hi,
On Oct 16, 2016, at 10:41 AM, CodeDmitry <dimamakhnin@gmail.com> wrote:
I was actually curious about this in Ruby as well, since Ruby also doesn't have the Smalltalk message syntax.
I figure that the magic behind it is that Smalltalk takes strings like "dict at: 'foo' put: 'bar'" and evaluates them into a JavaScript equivalent of "dict['at:put:']('foo', 'bar')â.
There is no magic. This is how the syntax is. Instead of parentheses that accumulate all parameters in a single place like: x.atput(a,b);
we can insert the argument inside the message name, like: x at: a put: b.
Itâs not magic, is just different. Thatâs all. Except that one makes it possible to write code that resembles natural language.
Doru
Basically, my proof of concept(I cheated with regex to cut time):
'use strict';
var Dictionary = function Dictionary() { this.dict = {}; }; Dictionary.prototype['at:put:'] = function(index, value) { this.dict[index] = value; }; Object.defineProperty(Dictionary, 'new', { get: function () { return new this; } });
function st_eval(str) { var x = str.match(/(dict)\s+(at):\s+'(foo)'\s+(put):\s+'(bar)'/); var target = x[1]; var callName = x[2] + ':' + x[4] + ':'; var argv = [x[3], x[5]]; var realTarget = eval(target); realTarget[callName].apply(realTarget, argv); }
var dict = Dictionary.new; console.log(dict); //dict['at:put:']('foo', 'bar'); console.log(dict); st_eval("dict at: 'foo' put: 'bar'"); console.log(dict);
-- Mark
On Sun, Oct 16, 2016 at 12:44 PM, CodeDmitry <dimamakhnin@gmail.com> wrote:
I understand that it is a single message send, but to know how to handle the message at runtime, the parser needs to somehow determine where the implementation of that message is. It must do a lookup based on multiple keys(at, and put),
This is wrong :) it is only one key #at:put: Define a method.... test |arr| a := Array new: 1. a at: 1 put: 'Hello, World'. Then in the method pane, right-click and choose Inspect Method, then look at the [Bytecodes] tab. You'll see "28 <C1> send: at:put:" So the parser has already done its part compiling the source code to bytecode and plays no further part in looking up which implementation method to execute in response to the message. That is done by the VM when the message is sent. If you look at the chart at the top of [1], you'll see #interpret calls #dispatchIn:On: which calls #commonOrdinarySend:. This calls #internalFindNewMethodOrdinary which starts the actual method lookup by the VM. To review these methods you need to load the VMMaker package, which the instructions at [2] will do for you. [1] http://forum.world.st/CogVM-Execution-Flow-td4900675.html [2] https://github.com/pharo-project/pharo-vm cheers -ben which is really confusing. "methods" are easy to look up
because they only have one name, but messages have one name which is split amongst each argument.
Simple concatenation does not make sense, since {at: x, put: y} and {a: x, tput: y} are different messages.
The only way I can think of this being implemented "easily" is by storing a table of entries {num_args, arg_names, impl_ref}, and then have the function "send" do a linear search over this table until it finds an entry with num_args = 2, each arg_name matching the argument name keys in order, and then calling the impl_ref with the array of values without the names.
I'm wondering if there is a way to do this without having to loop over every single message the object has, then every message its' parent object has, and so on, just to find which function you actually want to call.
-- View this message in context: http://forum.world.st/How-do-Smalltalk-disambiguate-messages-tp4918946p49189... Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
participants (8)
-
Ben Coman -
Charlie Robbats -
CodeDmitry -
Dimitris Chloupis -
Gabriel Cotelli -
Mark Bestley -
Nicolai Hess -
Tudor Girba