[Cuis-dev] Language constructs

Erik Stel erik.stel at gmail.com
Sun May 3 11:56:58 PDT 2020


Hi Phil,

I had the feeling you were looking for a mechanism to have a more ‘in-between’ model. Not only compile-time and not only run-time evaluation. Being able to create a literal, but update it afterwards (from anywhere, not only through source code). What I described allows you to change the literal at runtime and still be able to change the original method and NOT recreate the initial compile time literal. Normally changing a method and storing it, will recreate literals and destroy any runtime changes to this literal. You mentioned this in relation to layout structures, but might have meant another type of dynamics here. And what you’re describing now seems more in line with Juan’s remark about having immutable literals.

Anyways…it was just a brain teaser I had to try out.

Cheers,
Erik

> On 3 May 2020, at 20:44, Phil B <pbpublist at gmail.com> wrote:
> 
> Erik,
> 
> On Sun, May 3, 2020 at 4:44 AM Erik Stel <erik.stel at gmail.com <mailto:erik.stel at gmail.com>> wrote:
> Hi Phil,
> 
> This is (very far) outside the scope of my learning environment (ie this does not make things simpler for the newcomers): what if we would allow for the ‘literal’ to be changed at runtime and keep that value even if the source code is changed and the method is recompiled? This would allow for creating compile time objects, but also allow for dynamic (ie runtime) updates. Instead of trying to create a source code variant of the runtime value, just access the literal itself. It is a bit crazy, but maybe this is helpful for your use case. And it might make it clear where the information is going. I personally do not like the code change before saving…feels very hacky. Idea popped up in my mind, thought I try it out.
> 
> Interesting idea but I'm not sure what it gets me in this case.  The only issue I have with the current implementation is that it doesn't keep track of a single global instance of the literal when compiling.  I don't want to change the literals (in the sense that they are no longer #=), only ensure that they are identical (#==) to each other ASAP.  I do this almost immediately while building my image.  Once that's accomplished, I don't want to touch them again.  From the standpoint of the original source code, it got what it asked for and is none the wiser that it wasn't the actually the compiler that did the literal substitution.
> 
> This could be done at compile-time and would be 100% valid since there's no promise that I'm aware of that says literals will, or will not, be #==.  I'm just saying that they should be IMO since in most cases a literal is effectively an unlabeled constant... so why do we want N instances of them hanging around?  However, it does make the assumption that the literals you are doing this with are (at least effectively) immutable or you will likely have problems / strange results.  For mutable literals, which I'm not sure I'd even want, I would probably want a different syntax since they are a different animal.  Keep in mind that my original use case for all this was for immutable global constants and I specifically made Point immutable as part of my original proposal.
> 
> 
> When you write the following method:
> 
> MyClass class >> #myMethod
> 	^ `{ #a -> 1 . #b -> 2 . #c -> 4 }`
> 
> Let the compiler (ClassDescription actually) translate it (just before saving) to:
> 
> MyClass class >> #myMethod
> 	^ `((MyClass class >> #myMethod) literalAt: 1) ifNil: [ { #a -> 1 . #b -> 2 . #c -> 4 } asDictionary ]`
> 
> The thing is, #literalAt: will never answer nil (*) and it will therefore answer the (previously) stored object.
> 
> If I would change the Dictionary answered by #myMethod and then change the method to:
> 
> MyClass class >> #myMethod
> 
> 	3 + 4 inspect.
> 	^ `((MyClass class >> #myMethod) literalAt: 1) ifNil: [ { #a -> 1 . #b -> 2 . #c -> 4 } asDictionary ]`
> 
> The compiler will translate it (just before saving) to:
> 
> MyClass class >> #myMethod
> 
> 	3 + 4 inspect.
> 	^ `((MyClass class >> #myMethod) literalAt: 4) ifNil: [ { #a -> 1 . #b -> 2 . #c -> 4 } asDictionary ]`
> 
> That is, the literal index is updated, because 3, 4 and inspect are added as literals to this method.
> 
> Calling the method will answer the runtime object, but the original intention of the Dictionary with 3 elements remains visible.
> 
> I tried this out and it requires a few additions to the code and some source code mangling, but nothing really difficult. But haven’t tested it elaborately.
> 
> (*) unless the original compiled literal was nil which seems less logical, but could even have a use case. Maybe a value can’t be compiled at runtime and will receive its value 
> 
> Cheers,
> Erik
> 
>> On 1 May 2020, at 19:21, Phil B <pbpublist at gmail.com <mailto:pbpublist at gmail.com>> wrote:
>> 
>> Erik,
>> 
>> On Fri, May 1, 2020 at 5:15 AM Erik Stel <erik.stel at gmail.com <mailto:erik.stel at gmail.com>> wrote:
>> Hi Phil,
>> 
>> Thanks for explaining the history there. Helpful insight!
>> 
>> What is your motivation for using backticks (or actually the ad hoc literal creation)? Is it performance?
>> 
>> Mostly performance, partially clarity.  Let's use your example of Set.  That's something I do quite a bit for various kinds of collections.  Why have either a bunch of {1 2 3 4} asSet code constantly being unnecessarily executed or creating a bunch of ivar/cvar storage slots with helper methods to avoid it when I can just `{1 2 3 4} asSet` in the place I need it?  So I use it pretty much anywhere I know, and can create, what I need at compile time.  In using the backtick syntax, I'm making it clear that there's no need for runtime dynamism here: I need a set and can/do create it at compile time.  It's not a perfect solution: we essentially have a language construct with no tool support or syntactic sugar.
>> 
>> I actually go a bit further than Juan did in that I'm also going back through the image after my methods are compiled and ensuring things like `0 at 0` are #== to each other.  This allows for a number of performance optimizations (main reason) as well as reducing the image object count (bonus reason).
>>  
>> And I am intrigued by the ‘full macro system’ you envision(ed). Could you elaborate a bit on that? What would you have wanted? And what does it offer the current language (constructs) and tools don't offer?
>> 
>> Something I really like about Smalltalk is the uniformity of its objects re: message sending.  Something I really dislike is it's absolute focus on doing just about everything at runtime even when you know all or at least part of what you need to at compile-time.  `` is useful when you know *everything* at compile time, macros are for when you know *part* of it.  I wouldn't want much, just what Lisp gives me in a an OO way ;-)
>> 
>> An example use case is wanting to define a Morph layout hierarchy, or some other complex structure, at compile-time.  You know everything you need to do, but you don't want to tediously type in all of the code to make it happen or have to execute most of it at runtime.  While you can create a bunch of helper methods to minimize the work, it's a sub-optimal solution.
>> 
>> Ideally, you'd just want to provide the details that vary, assemble everything you can at compile time, and only do the last bit that needs to be dynamic at runtime.  How this would likely look in practice is having some strange looking quoting syntax, a whole bunch of intermediate literal objects, and a strange looking final bit of code that executes at runtime to stitch the pieces together.
>> 
>> 
>> Squeak and Pharo compatibility are no concern here (for me). I was referring to it as explanation that backticks did not arrive (historically) in Cuis because of possible ancestors/nephews/… My earlier question about naming referred to Pharo because IF possible I would like to write code that does not require much ‘porting’. But keeping compatibility is again not a major concern for me.
>> 
>> Once you get move beyond a handful of core objects, you'll find that in many areas, this gets geometrically harder as you add more functionality.  Just follow the recent conversations re: Hilaire's porting of Dr. Geo.  There are a lot of subtle and not-so-subtle differences in class and method names to just flat-out conceptual differences in approach of what's in the Cuis/Squeak/Pharo images today.  They continue to diverge over time.  We're essentially three distinct groups of people living on three different islands.  Sure, from time to time we'll visit each other's island and steal some ideas, but we're all operating independently aside from running on a common VM.
>> 
>> 
>> I’m in the proces of building a learning environment for kids based on Smalltalk. So I would prefer a Smalltalk-implementation that is very 'simple', lean and mean (and therefore Cuis seems a good candidate). Maybe it is difficult to have a single language that fulfils both a role for learning as well as a role for skilled and experienced developers that want a high efficiency in/during coding. Because the idea behind Smalltalk is to learn from the system itself, having more language constructions is ‘unwanted’ because more has to be learned. I wonder whether the tool support could help here. What if the CodeEditor would allow for quickly creating things like the Array (or Set) construction and have a pleasing way of displaying this for reading, while still being very clear in that it is actually a regular Array (or Set) construction? As you might understand from this rambling I’m still searching for some answers how I want to do things.
>> 
>> Opinions vary on this.  Most seem hell-bent on using Smalltalk syntax for everything and doing it all at runtime.  I'm in the camp that says that doesn't scale and that DSLs (including new syntax and doing things at compile-time as needed) are a better way.
>> 
>> 
>> Thx for taking time to explain!
>> 
>> Regards,
>> Erik
>> 
>>> On 1 May 2020, at 10:19, Phil B <pbpublist at gmail.com <mailto:pbpublist at gmail.com>> wrote:
>>> 
>>> Erik,
>>> 
>>> On Fri, May 1, 2020 at 3:46 AM Erik Stel via Cuis-dev <cuis-dev at lists.cuis.st <mailto:cuis-dev at lists.cuis.st>> wrote:
>>> Maybe I wasn’t clear (because it was part of another topic, see below) or tread on a sensitive subject, but I’m still eager to hear the reasoning for having backticks (which are not in Squeak nor Pharo) from the simplicity point of view. Would anyone care to elaborate?
>>> 
>>> 
>>> Years ago I noticed that we had a lot of pointless dynamism in the image especially since moving to local coordinates (i.e. we had 0 at 0 all over the image.)  In an attempt to *not* extend the language,  I proposed Point class>>zero for effectively a singleton 0 at 0 instance.  Juan surprised me and effectively said 'I don't like that, let's go this way instead' (i.e. backticks).  It was a pretty elegant and minimal solution so I didn't have a problem with it at the time and it has definitely grown on me.  While addressing 0 at 0 was the initial motivation, it is useful anywhere you want to create ad hoc literals.  I use it a ton and only wish we went a little further and had a full macro system in Smalltalk ;-)
>>> 
>>> As far as compatibility with Squeak and Pharo... well that's extremely problematic IMO.  Pharo changes things all the time (and not always for the better) seemingly based on the weather.  So any attempt to keep in sync with it would mean breaking Cuis whether or not we thought the change was a good idea.  Squeak has the opposite problem: it doesn't change much at all.  To some, this is an asset, to me it's a liability: I don't mind working with an obscure/fringe language, I do mind working with a dead language.  To me, Smalltalk-80 was great 40 years ago but should not be the final stop in language evolution.
>>> 
>>> 
>>> I am also eager to know what others think about language constructs such as #(), {} and `` for daily usage. And I mean this in the sense ‘Do you use these often? Could you live without them?’. I do understand how they can be used and what their meaning is ;-). And I can also lookup their current use in the default image, but that does not answer how you/we use them in our (application) code.
>>> 
>>> I tend to use {} more than #(), but I do use both of those as well.  My only gripe is that all of the damned brackets have been used up by Smalltalk (as most other languages do as well)... I really would have liked to have at least one set of brackets that were available for 'user-defined' purposes but, oh well.
>>>  
>>> 
>>> Kind regards,
>>> Erik
>>> 
>>> 
>>> Thanks,
>>> Phil 
>> 
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.cuis.st/mailman/archives/cuis-dev/attachments/20200503/07608f42/attachment-0001.htm>


More information about the Cuis-dev mailing list