You know, literals are quite useful in case when you want to shorten some object initialization. For example #() and {} for arrays and $[]for byte arrays. However, if there is a way to add custom literals, for example for sets (something like #{} I guess)? how to do it? and can some special kind of objects for creating literals easily be added to Pharo?
|
Not really.
You can use ClassVariables though. Here's an example for Sets: Object subclass: #MyClass instanceVariableNames: '' classVariableNames: 'MyClassVar' package: 'MyPackage' MyClass class>>initialize super initialize. MyClassVar := Set new. MyClass>>foo self bar: MyClassVar Obviously sets are not as easy to deal with. You cannot mutate empty arrays/bytearrays, if you concatenate something it creates a new object. You can add things in sets. So you need to be careful... You can make the object read-only to avoid issues (MyClassVar beReadOnlyObject). On Fri, Apr 27, 2018 at 3:19 PM, Debiller 777 <[hidden email]> wrote:
|
Hi Clément,
On 27/04/2018 10:32, Clément Bera wrote: > Obviously sets are not as easy to deal with. You cannot mutate empty > arrays/bytearrays, if you concatenate something it creates a new object. > You can add things in sets. So you need to be careful... You can make > the object read-only to avoid issues (MyClassVar beReadOnlyObject). Sorry, but I can't understand how a class variable could allow you to use a new literal. As far as I knew the only way to have new literals was to modify the compiler. So if you want #{} to create a new empty set, or #{1 1 2} to create a Set with 1 and 2 as elements, there is no way to do it. Or this %{$a -> 1. 'foo' -> false. 'baz' -> #{1 1 2}} to create a dictionary with such set as literal. :) Regards, -- Esteban A. Maringolo |
Administrator
|
On Fri, Apr 27, 2018 at 2:08 PM, Esteban A. Maringolo <[hidden email]> wrote: Hi Clément, Yes. VA Smalltalk has an interesting syntax extension which allows an arbitrary expression to be a compile-time literal. Going by memory, something like ##(Dictionary new at: #a put: self something; at: #b put: self somethingElse; yourself). Of course, the problem with that is it will not change if the implementations of the messages it sends entail some changes over time.
|
On 27/04/2018 15:35, Richard Sargent wrote:
> On Fri, Apr 27, 2018 at 2:08 PM, Esteban A. Maringolo > <[hidden email] <mailto:[hidden email]>> wrote: > As far as I knew the only way to have new literals was to modify the > compiler. > > > Yes. > > VA Smalltalk has an interesting syntax extension which allows an > arbitrary expression to be a compile-time literal. > Going by memory, something like ##(Dictionary new at: #a put: self > something; at: #b put: self somethingElse; yourself). Dolphin Smalltalk provides that ##() literal as well since a decade at least, so you have the full expression that gave origin to that literal, but you don't need to compute it every time it is accessed. Although I don't remember seeing it with Dictionaries, but more with string concatenations or "magic numbers" like seconds in a day stored as ##(24 * 60 * 60). Regards, -- Esteban A. Maringolo |
The guy who asked the question said: "...when you want to shorten some object initialization" Using ClassVariable is an alternative way to shorten object initialization, reading a ClassVariable is almost the same performance as reading a literal, so that looked like a good alternative to me. Both the ClassVariable and the literal have the same issues (same object so if you mutate it you have to deal with it). But yes, it's not a new literal. For new literals, you can extend the compiler or hard patch thing: MyClass>>foo ^ #bar (MyClass>>#foo) literalAt: ((MyClass>>#foo) literals indexOf: #bar) put: Set new. MyClass new foo >>> a Set () Obviously it depends what you mean by literal, the latter code uses the literal bytecode instruction, which does not make the pushed object a literal object... On Fri, Apr 27, 2018 at 8:59 PM, Esteban A. Maringolo <[hidden email]> wrote: On 27/04/2018 15:35, Richard Sargent wrote: |
oh, you were talking about how te VM views the object,
I was thinking in terms of how the compiler sees the text. So I'm talking about literals "syntax". Thanks anyway for the "trick" :) On 27/04/2018 16:34, Clément Bera wrote: > The guy who asked the question said: "...when you want to shorten some > object initialization" > > Using ClassVariable is an alternative way to shorten object > initialization, reading a ClassVariable is almost the same performance > as reading a literal, so that looked like a good alternative to me. Both > the ClassVariable and the literal have the same issues (same object so > if you mutate it you have to deal with it). > > But yes, it's not a new literal. > > For new literals, you can extend the compiler or hard patch thing: > > MyClass>>foo > ^ #bar > > (MyClass>>#foo) literalAt: ((MyClass>>#foo) literals indexOf: #bar) put: > Set new. > > MyClass new foo > >>>> a Set () > > Obviously it depends what you mean by literal, the latter code uses the > literal bytecode instruction, which does not make the pushed object a > literal object... -- Esteban A. Maringolo |
Administrator
|
On Fri, Apr 27, 2018 at 3:58 PM, Esteban A. Maringolo <[hidden email]> wrote: oh, you were talking about how te VM views the object, Another common technique is a "once block". Teach Block how to evaluate itself once and to use its cached result the next time. It's
not as fast as a literal, but it is a pretty effective way to have a
clean implementation with as complex code as one could wish.[Dictionary new at: #a put: self something; at: #b put: self somethingElse; yourself] once
|
In reply to this post by Clément Béra
On 28 April 2018 at 03:34, Clément Bera <[hidden email]> wrote:
Maybe pool dictionaries provide a half-way house... cheers -ben |
CONTENTS DELETED
The author has deleted this message.
|
The ## syntax that some Smalltalks have (Smalltalk/X, for example, which calls it a Dolphin extension) is related to Common Lisp's #.e the result of evaluating e at *read* time, taken as a literal #,e the result of evaluating e at *load* time, taken as a literal. This reminds us there are several times that a ## form could be evaluated: - in the parser, when a method is parsed (before code generation) - when code is generated - when code is generated AND whenever the image is restored - when the value is first needed (I'd prefer this) - when the value is first needed in a thread (that is, each thread that encounters it will evaluate it just once, so each thread may see a different value). One continuing annoyance in Smalltalk is that I cannot put a Fraction, a Date, a Time, or a DateAndTime in a literal. It would be nice to have #1969-07-20 or #1/2 or #16:40:32 as literals and they contain no expressions that could result in any confusion about when they are evaluated and cannot be changed any more than a character literal can. Curly brace syntax {e1. ... en} is an expression form, not a literal form. Similar forms for sets of several kinds and dictionaries of several kinds are supported in my Smalltalk, and they are indeed useful, but they are especially useful because they are *not* literals but create new values every time. On 28 April 2018 at 16:13, lb <[hidden email]> wrote:
|
What would entail actually extending the syntax and adding the set literal? From my brief observations (when I was discussing this with OP on discord): * adding a new AST node * extend RBParser * extend IRMethod/IRTranslator... maybe generating bytecode for `{ ... } asSet` instead would be enough? (not sure how debugger would react to this) anything else? Would VM changes be required? Peter On Sat, Apr 28, 2018 at 8:33 AM, Richard O'Keefe <[hidden email]> wrote:
|
A On 28 April 2018 at 20:21, Peter Uhnák <[hidden email]> wrote:
'{' binarySelector expr '.' ... '.' expr ['.'] '}' to my compiler because I was sick of other people being able to write stuff more elegantly in Python than I could in Smalltalk. Here the binarySelector controls what kind of set: = Set == IdentitySet ?= CaseInsensitiveSet < SortedSet (splay trees) > SortedSet (with comparison reversed) <> TernarySet (ternary search string for strings). I'm not happy with the syntax, but it's the least ugly I've been able to find. Having syntax only for Set would be so limiting there'd be no real point. So. No change to the scanner. No change to the abstract syntax trees. {== x. y} generates the same syntax tree that ((IdentitySet new: 2) add: x; add: y; yourself) would have done. And therefore no change to the back end code generator. The *only* change was the parsing routine that responds to '{'. The fundamental issue is WHY you want an extension to the syntax. In my case, the {binarySelector exprs} syntax for sets and corresponding #{binarySelector maplets} syntax for dictionaries were to let me approximate the brevity of Python and Javascript. I didn't need new *semantics*. In contrast, the ##(expr) syntax in Dolphin, Smalltalk/X, and whatever others does NOT offer brevity at all but DOES offer new semantics. To the limited, not to say derisory, extent to which I understand the Dolphin compiler, it does have a special StOptimizedNode type of AST node for ##(...) forms. I don't *think* any VM changes are needed, unless of course you want to be able to decompile the byte-code and recover the ##(expr) source. I don't have a Windows box any more, so I can't run Dolphin, sorry, just admire it. I can say that it takes care not to put literals inside a ##(expr) form in the literal table. |
In reply to this post by Richard Sargent
Hi. сб, 28 апр. 2018 г., 1:26 Richard Sargent <[hidden email]>:
We have similar mechanizm in Pharo. Look at http://dionisiydk.blogspot.fr/2016/07/magic-with-pharo-reflectivity.html
|
Administrator
|
In reply to this post by Debiller 777
Debiller 777 wrote
> You know, literals are quite useful in case when you want to shorten some > object initialization. For example #() and {} for arrays and $[]for byte > arrays. However, if there is a way to add custom literals, for example for > sets (something like #{} I guess)? how to do it? and can some special kind > of objects for creating literals easily be added to Pharo? The discussion on this topic has been quite interesting. However, I want to circle back to this original question; I think we've wandered from it in some respects. The original motivation was "when you want to shorten some object initialization". Typically, this kind of driver is a performance issue. As in, "I want to be able to initialize my instances with minimal overhead" so that e.g. the cost of creating instances is minimized and I can create and discard instances easily and frequently. I'm not saying that is the only motivation, but it is largely what I will address here. A number of messages discussed somewhat more elaborate syntax for literals, such as the example for a date #{'2018-05-01') or something close to that. And someone else will surely want #{'14:30'} for much the same justification. And yet others will want something else altogether. My of my all-time favourite design rules was something I read in 1989, loosely called the 0, 1, infinity rule. As soon as we go beyond one of something, there are an arbitrary number of possibilities we must deal with. That's not a burden we want to put on the compiler (or more likely on the parser). We could imagine that the syntax is a little more elaborate, such as #{Date '2018-05-01'} in which case the parser could automatically send the #fromString: message to the receiver Date using the argument of the string. Such an approach would give a general solution to a simple subset of the possible initializers one might encounter. However, it wouldn't handle other kinds of initialization, such as the Dictionary example I mentioned in my earlier response. It also wouldn't handle the issue of the implementation of #fromString: being changed and failing to recompile the literal that was generated independently of it. For the sake of argument, I think we can reasonably assert that #fromString: would be assumed to be correct and any changes in its implementation would not impact the literal that was previously and correctly created from that literal string. So, we have a proposal that with a small increase in the syntactic burden of the language does address a reasonable subset of the originally stated problem. But, it doesn't solve the general problem. For that reason alone, I don't agree with increasing the syntax of the language for it. Another respondent mentioned pool variables. These have the benefit that the name can be entirely informative and their values can be recomputed any time one wishes. In fact, they may already be the correct and complete answer. They aren't without their own costs, of course. Obviously, they must be defined before they can be referenced in the code that uses them. This pretty much requires a multiple package approach, requiring one package to define the pool variables (perhaps with only a place-holder value) and another package with the code that needs them. (VA Smalltalk has a technique that allows them to be defined in the same package, but at a terrible price: their definitions are opaque to the standard Smalltalk tools. We can discuss this on a separate thread if anyone wants to know more about that.) There is also the "once" block that I mentioned elsewhere. It typically uses a dictionary to cache the result of the first evaluation and then provide the look up on subsequent evaluations. One could also add a cached result instance variable to Block and avoid such a look up. It's still not as fast as a pool variable, of course. As a side note, I would like to remind everyone about Kent Beck's comments on literals. I'm paraphrasing him, but essentially there are only a very few literals which should be allowed to run around naked. The may be 0, 1, '', and possibly -1. Certainly every other literal should be encapsulated in an intention revealing name. Does May 1st mean something? If one is going to use it as I did in the examples above, it had better be given a name that everyone will be able to understand. For example, any reader would understand Float pi and the distinction between it and Double pi. A string of random digits takes considerably more parsing on the part of the reader to understand its intent. Lastly, I would encourage the original poster to elaborate on what problem or problems he is really trying to solve. (Over the years, I ave encountered many users who request a feature to solve a problem that isn't the real problem they need to solve. Programmers, too, make the same mistake.) -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html |
Administrator
|
In reply to this post by Denis Kudriashov
Denis Kudriashov wrote
> We have similar mechanizm in Pharo. Look at > http://dionisiydk.blogspot.fr/2016/07/magic-with-pharo-reflectivity.html Wow! That's really cool. I like that we can do it without adding *any* syntax. Tiny syntax is one of the more special things about the Smalltalk language. ----- Cheers, Sean -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html
Cheers,
Sean |
Free forum by Nabble | Edit this page |