Would any of the "pipe" advocates mind taking a stab at the *current* source methods, and rewrite the method showing how pipe syntax would have simplified or clarified the method?
I ask this because I suspect that if you're following good practices (such as those adovocated in Beck's "Smalltalk Best Practice Patterns"), you won't actually *need* a pipe syntax, because your code would never have gotten that complicated.
So, instead of writing Smalltalk with a bias for your previous programming language where pipe makes more sense, how about taking some *native* Smalltalk to show how pipe would have helped?
Hi Randal,
Thanks for the suggestion.
To be clear, even though I support using the symbol ';;' as pipe operator IF we put it in, I doubt I would actually ever use it. I prefer parenthesis, and agree that if you are many levels nested you probably need to ether create more objects to model your complicated behavior, or you need to add temps to make it easier to read your code.
On the other hand I find the cascade extremely useful, especially for creation methods. I find chaining more tolerable when the object doesn't change. It just makes more sense. For the same reason I usually look at long chained methods as an indication that the code is probably written on the wrong class and needs to be cleaned up.
Ron
-----Original Message----- From: Randal L. Schwartz
Would any of the "pipe" advocates mind taking a stab at the *current* source methods, and rewrite the method showing how pipe syntax would have simplified or clarified the method?
I ask this because I suspect that if you're following good practices (such as those adovocated in Beck's "Smalltalk Best Practice Patterns"), you won't actually *need* a pipe syntax, because your code would never have gotten that complicated.
So, instead of writing Smalltalk with a bias for your previous programming language where pipe makes more sense, how about taking some *native* Smalltalk to show how pipe would have helped?
-- Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 merlyn@stonehenge.com URL:http://www.stonehenge.com/merlyn/ Perl/Unix/security consulting, Technical writing, Comedy, etc. etc. See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
It's not like you even need a new syntax to avoid the paranthesis - just new kinds of objects, eg:
nonHermsOlderThanTensMothersNames := lotsOfPeople selecting: [:each | each age > 10]; rejecting: [:each | each sex = #unknown]; collecting: [:each | each mother]; selecting: [:each | each notNil]; collecting: [:each | each name]; asOrderedCollection
No new syntax, just new objects. Has this already been covered in the thread? if so, sorry for bringing it up again.
Michael
Ron Teitelbaum wrote:
Hi Randal,
Thanks for the suggestion.
To be clear, even though I support using the symbol ';;' as pipe operator IF we put it in, I doubt I would actually ever use it. I prefer parenthesis, and agree that if you are many levels nested you probably need to ether create more objects to model your complicated behavior, or you need to add temps to make it easier to read your code.
On the other hand I find the cascade extremely useful, especially for creation methods. I find chaining more tolerable when the object doesn't change. It just makes more sense. For the same reason I usually look at long chained methods as an indication that the code is probably written on the wrong class and needs to be cleaned up.
Ron
-----Original Message----- From: Randal L. Schwartz
Would any of the "pipe" advocates mind taking a stab at the *current* source methods, and rewrite the method showing how pipe syntax would have simplified or clarified the method?
I ask this because I suspect that if you're following good practices (such as those adovocated in Beck's "Smalltalk Best Practice Patterns"), you won't actually *need* a pipe syntax, because your code would never have gotten that complicated.
So, instead of writing Smalltalk with a bias for your previous programming language where pipe makes more sense, how about taking some *native* Smalltalk to show how pipe would have helped?
-- Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 merlyn@stonehenge.com URL:http://www.stonehenge.com/merlyn/ Perl/Unix/security consulting, Technical writing, Comedy, etc. etc. See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
I haven't seen it, good catch.
On 8/27/07, Michael Lucas-Smith mlucas-smith@cincom.com wrote:
It's not like you even need a new syntax to avoid the paranthesis - just new kinds of objects, eg:
nonHermsOlderThanTensMothersNames := lotsOfPeople selecting: [:each | each age > 10]; rejecting: [:each | each sex = #unknown]; collecting: [:each | each mother]; selecting: [:each | each notNil]; collecting: [:each | each name]; asOrderedCollection
No new syntax, just new objects. Has this already been covered in the thread? if so, sorry for bringing it up again.
Michael
Ron Teitelbaum wrote:
Hi Randal,
Thanks for the suggestion.
To be clear, even though I support using the symbol ';;' as pipe operator IF we put it in, I doubt I would actually ever use it. I prefer parenthesis, and agree that if you are many levels nested you probably need to ether create more objects to model your complicated behavior, or you need to add temps to make it easier to read your code.
On the other hand I find the cascade extremely useful, especially for creation methods. I find chaining more tolerable when the object doesn't change. It just makes more sense. For the same reason I usually look at long chained methods as an indication that the code is probably written on the wrong class and needs to be cleaned up.
Ron
-----Original Message----- From: Randal L. Schwartz
Would any of the "pipe" advocates mind taking a stab at the *current* source methods, and rewrite the method showing how pipe syntax would have simplified or clarified the method?
I ask this because I suspect that if you're following good practices (such as those adovocated in Beck's "Smalltalk Best Practice Patterns"), you won't actually *need* a pipe syntax, because your code would never have gotten that complicated.
So, instead of writing Smalltalk with a bias for your previous programming language where pipe makes more sense, how about taking some *native* Smalltalk to show how pipe would have helped?
-- Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 merlyn@stonehenge.com URL:http://www.stonehenge.com/merlyn/ Perl/Unix/security consulting, Technical writing, Comedy, etc. etc. See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
Michael Lucas-Smith wrote:
It's not like you even need a new syntax to avoid the paranthesis - just new kinds of objects, eg:
nonHermsOlderThanTensMothersNames := lotsOfPeople selecting: [:each | each age > 10]; rejecting: [:each | each sex = #unknown]; collecting: [:each | each mother]; selecting: [:each | each notNil]; collecting: [:each | each name]; asOrderedCollection
No new syntax, just new objects. Has this already been covered in the thread? if so, sorry for bringing it up again.
But it's not fully equivalent.
Adding new methods that return results (and not self) must be done at least once per selector--and requires a naming convention that associates the "return receiver" with the "return result" version of each selector (and there would be many cases where the distinction wouldn't matter.)
Adding a new "Pipe" class (e.g., as in Bert's "Squeak asPipe" example) results in an implementation that isn't thread safe (if the Pipe object isn't private to a single thread.) And it's not as efficient as syntax-based piping.
The pipe operator is a single, always thread-safe, efficient change. But that doesn't mean I endorse it. I am still considering the matter.
I agree (also with what Randal said). I view myself as someone who uses functional paradigms quite often, but Smalltalk is not a functional language. It's a beautiful blend of pure OO and higher order programming. Just as a solution would look different in Haskell then e.g. Lisp, it will look different in Smalltalk too.
On 8/27/07, Ron Teitelbaum Ron@usmedrec.com wrote:
Hi Randal,
Thanks for the suggestion.
To be clear, even though I support using the symbol ';;' as pipe operator IF we put it in, I doubt I would actually ever use it. I prefer parenthesis, and agree that if you are many levels nested you probably need to ether create more objects to model your complicated behavior, or you need to add temps to make it easier to read your code.
On the other hand I find the cascade extremely useful, especially for creation methods. I find chaining more tolerable when the object doesn't change. It just makes more sense. For the same reason I usually look at long chained methods as an indication that the code is probably written on the wrong class and needs to be cleaned up.
Ron
-----Original Message----- From: Randal L. Schwartz
Would any of the "pipe" advocates mind taking a stab at the *current* source methods, and rewrite the method showing how pipe syntax would have simplified or clarified the method?
I ask this because I suspect that if you're following good practices (such as those adovocated in Beck's "Smalltalk Best Practice Patterns"), you won't actually *need* a pipe syntax, because your code would never have gotten that complicated.
So, instead of writing Smalltalk with a bias for your previous programming language where pipe makes more sense, how about taking some *native* Smalltalk to show how pipe would have helped?
-- Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 merlyn@stonehenge.com URL:http://www.stonehenge.com/merlyn/ Perl/Unix/security consulting, Technical writing, Comedy, etc. etc. See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
"Randal L. Schwartz" merlyn@stonehenge.com wrote in message news:86y7fwalgn.fsf@blue.stonehenge.com...
Would any of the "pipe" advocates mind taking a stab at the *current* source methods, and rewrite the method showing how pipe syntax would have simplified or clarified the method?
I wouldn't describe myself as an 'advocate', but I don't mind exploring the concept. So here is an example..
Collection has a helper method #select:thenCollect: . Looking at the senders of that, I found ChangeSet class>>highestNumberedChangeSet (I have reformatted the source to make things clearer)
highestNumberedChangeSet "ChangeSorter highestNumberedChangeSet" | aList | aList := self allChangeSetNames select: [:aString | aString startsWithDigit] thenCollect: [:aString | aString initialIntegerOrNil]. ^aList size > 0 ifTrue: [aList max] ifFalse: [nil]
With pipes, this could be written as
highestNumberedChangeSet "ChangeSorter highestNumberedChangeSet" ^self allChangeSetNames select:[:aString | aString startsWithDigit] ;; collect:[:aString | aString initialIntegerOrNil] ;; ifNotEmpty:[:list | list max]
The problem with these kind of helper methods is that there many other permutations (collect:thenDo:, collect:thenSelect: select:thenDo: select:thenCollect:thenDetect: etc.). Either, methods are created for all permutations, and most of them never actually used. (Collection>>collect:thenDo: has no senders). Or, only those with senders are added, and the question is asked, for exanple, :- Why is there a #select:thenDo: but no #collect:thenDo: ?
With the pipe you get all permutations, without needing to create methods for them all.
Here is another example; a snippet from Morph>>renameTo:
classes := (self systemNavigation allCallsOn: assoc) collect: [:each | each classSymbol]. classes asSet do: [:clsName | (Smalltalk at: clsName) replaceSilently: oldName to: aName].
which, with pipes, could be rewritten as...
self systemNavigation allCallsOn: assoc ;; collect: [:each | each classSymbol] ;; asSet ;; do: [:clsName | (Smalltalk at: clsName) replaceSilently: oldName to: aName].
Cheers, Andy
With pipes, this could be written as
highestNumberedChangeSet "ChangeSorter highestNumberedChangeSet" ^self allChangeSetNames select:[:aString | aString startsWithDigit] ;; collect:[:aString | aString initialIntegerOrNil] ;; ifNotEmpty:[:list | list max]
With pipe objects using standard smalltalk syntax, this could be written as:
highestNumberedChangeSet "ChangeSorter highestNumberedChangeSet" ^self allChangeSetNames asPipe selecting: [:aString | aString startsWithDigit]; collecting: [:aString | aString initialIntegerOrNil]; ifNotEmpty: [:list | list max]
which, with pipes, could be rewritten as...
self systemNavigation allCallsOn: assoc ;; collect: [:each | each classSymbol] ;; asSet ;; do: [:clsName | (Smalltalk at: clsName) replaceSilently: oldName to: aName].
And again:
(self systemNavigation allCallsOn: assoc) asPipe collecting: [:each | each classSymbol]; selectingAsUniqueSet; do: [:clsName | (Smalltalk at: clsName) replaceSilently: oldName to: aName]
I guess I should point out that work like this has been done in VisualWorks from two fronts - StreamWrappers and ComputingStreams. Both packages are available in public store.
Pipes are a great idea - streams talking to streams is the only way to do efficient large-data-set programming (eg: google's map+reduce technique).
I wish more of Smalltalk were written with this approach in mind, it'd scale without effort then.. and programmers wouldn't accidently create memory explosion bottlenecks without trying. Multiple select:, collect:, reject: calls on large data sets will bring any image to its knees in seconds if more than one concurrent user invokes the same sort of operation at once.
The speed issue comes not from the time it takes to do one call of this - but what happens when multiple processes try to do the same thing (eg: multiple users hitting your server at once). And the speed issue comes in not from computing CPU cycles, but from memory allocation and garbage collection.
If you start with a collection of 100,000 things, you do 4 operations on it - three collect:'s and a reject:.. you'll probably be allocating 4 arrays of 100,000 slots. That's 1.2mb's of data you've just allocated. Now get 10 users using the same function at the same time and you've just made 12mb's of data. Scale it up a little more elaborate chains of functions or more users and you have serious scalability issues.
Now to put the point home - if you are generating web pages from the server. You start with a parse of a node tree which concatenates dozens of little strings together to produce the page - which pushes it through a zip library - which pushes it through a chunks stream - perhaps there's a utf8 encoder in there too. Unless all those streams are using a cyclic buffer, streams to streams, they're going to be generating LOTS of small and big strings as they build up their own internal streams and buffers.
Anyway.. just food for thought. At Wizard we spent a considerably amount of time optimizing our Http/1.1 server to deal with exactly this sort of thing. We also found we could use the same code for database operations too (we were using BerkeleyDB as our database, so querying was done by us).
Cheers, Michael
On Aug 27, 2007, at 13:38 , Michael Lucas-Smith wrote:
With pipes, this could be written as
highestNumberedChangeSet "ChangeSorter highestNumberedChangeSet" ^self allChangeSetNames select:[:aString | aString startsWithDigit] ;; collect:[:aString | aString initialIntegerOrNil] ;; ifNotEmpty:[:list | list max]
With pipe objects using standard smalltalk syntax, this could be written as:
highestNumberedChangeSet "ChangeSorter highestNumberedChangeSet" ^self allChangeSetNames asPipe selecting: [:aString | aString startsWithDigit]; collecting: [:aString | aString initialIntegerOrNil]; ifNotEmpty: [:list | list max]
With the generic pipe object from my change-set in the original thread this gets you both - no need to define new methods:
highestNumberedChangeSet "ChangeSorter highestNumberedChangeSet" ^self allChangeSetNames asPipe select:[:aString | aString startsWithDigit]; collect:[:aString | aString initialIntegerOrNil]; ifNotEmpty:[:list | list max]
which, with pipes, could be rewritten as...
self systemNavigation allCallsOn: assoc ;; collect: [:each | each classSymbol] ;; asSet ;; do: [:clsName | (Smalltalk at: clsName) replaceSilently: oldName to: aName].
And again:
(self systemNavigation allCallsOn: assoc) asPipe collecting: [:each | each classSymbol]; selectingAsUniqueSet; do: [:clsName | (Smalltalk at: clsName) replaceSilently: oldName to: aName]
and again, too:
self systemNavigation asPipe allCallsOn: assoc; collect: [:each | each classSymbol]; asSet; do: [:clsName | (Smalltalk at: clsName) replaceSilently: oldName to: aName].
- Bert -
OK, now I'm confused. <s>
I thought if you had:
a msg | msg1 | msg2
msg1 went to the object (we'll call it "b") returned by "a msg", and msg2 went to the object created by "b msg1". In other words, the equivalent of:
b := a msg. c := b msg1. c msg2.
But this to me:
highestNumberedChangeSet "ChangeSorter highestNumberedChangeSet" ^self allChangeSetNames asPipe select:[:aString | aString startsWithDigit]; collect:[:aString | aString initialIntegerOrNil]; ifNotEmpty:[:list | list max]
seems more like:
b := a msg. b msg1. b msg2.
Do I misunderstand? (And at what point does the potential for confusion and ambiguity outweigh the feature?)
===Blake===
Blake wrote:
b := a msg. b msg1. b msg2.
Do I misunderstand? (And at what point does the potential for
confusion and ambiguity outweigh the feature?)
Yes.
The message sent to a is #asPipe, not msg:
p := a asPipe. p msg. p msg1. p msg2
Or alternatively:
a asPipe msg; msg1; msg2
The Pipe arranges for each message to be sent to the right object.
<snip>
With the generic pipe object from my change-set in the original thread this gets you both - no need to define new methods:
highestNumberedChangeSet "ChangeSorter highestNumberedChangeSet" ^self allChangeSetNames asPipe select:[:aString | aString startsWithDigit]; collect:[:aString | aString initialIntegerOrNil]; ifNotEmpty:[:list | list max]
Yes. I am coming to the conclusion that there is no need for additional syntax; your solution works really well. I would suggest, however, that Pipe is made a subclass of ProtoObject rather than Object. This will avoid oddities such as...
(1 asPipe + 1; + 1; = 3) evaluates to false
but with Pipe a subclass of ProtoObject, it evaluates to true, as expected.
Cheers, Andy
On Aug 27, 2007, at 17:32 , Andrew Tween wrote:
<snip> > With the generic pipe object from my change-set in the original > thread this gets you both - no need to define new methods: > > highestNumberedChangeSet > "ChangeSorter highestNumberedChangeSet" > ^self allChangeSetNames asPipe > select:[:aString | aString startsWithDigit]; > collect:[:aString | aString initialIntegerOrNil]; > ifNotEmpty:[:list | list max]
Yes. I am coming to the conclusion that there is no need for additional syntax; your solution works really well. I would suggest, however, that Pipe is made a subclass of ProtoObject rather than Object. This will avoid oddities such as...
(1 asPipe + 1; + 1; = 3) evaluates to false
but with Pipe a subclass of ProtoObject, it evaluates to true, as expected.
Sure. It just immensely improves debuggability if you start with an Object subclass first ;) Also, the reasonable use cases would usually involve only methods that are not in Object. Provided someone would actually want to play these meta games ;)
- Bert -
On Aug 28, 2007, at 12:30 AM, Bert Freudenberg wrote:
With the generic pipe object from my change-set in the original thread this gets you both - no need to define new methods:
highestNumberedChangeSet "ChangeSorter highestNumberedChangeSet" ^self allChangeSetNames asPipe select:[:aString | aString startsWithDigit]; collect:[:aString | aString initialIntegerOrNil]; ifNotEmpty:[:list | list max]
I have to admit that the asPipe idea is really cool and it might do the trick.
A minor minor thing: you still need to explain it, and it's better to have 1 syntax token attached to 1 meaning. Is a bit ugly that cascade changes it's meaning: sometimes does X, some times does Y.
Still the asPipe is a very very nice hack.
I think the question is different from adding or not syntax to do a trick.
Do Small-talkers want to assert:
"Use functional programming when possible"
or not ?
This is question. And I can't answer that.
It's not : "Shall we add a new syntax token to do the trick ?"
Fabio FIlasieno
PS. Note that I associate functional programming with a chained application of functions (better if pure)
For this Smalltalker, I don't start with a functional approach, I start with an object-oriented approach. Compared to functional, I have lots of state (class names and instance variables).
To the extent that I program functionally, it is usually inside of a method (eliminating temps). When I show this style to Java programmers (or use this style on a Java project), it is not well received. So I assign to temps and stay away from parens and chains (or pipes). I believe Joe Winchester calls this debugger friendly coding.
asPipe appeals to me because it doesn't change the syntax of the language. This Smalltalker takes pride in having a tiny core language that has remained mostly unchanged. I doubt I would be confused about asPipe cascades because I would see the receiver is the pipe object. This also makes it easier to port (as long as your dialect doesn't already have an asPipe implementation).
I would like to use asPipe in my own code (just did the equivalent of an asPipe reject:;reject:;select: with parenthesis). If the syntax changed version were adopted for Squeak, I might use it if I wasn't concerned about ports to other Smalltalks.
On 8/28/07, Fabio Filasieno fabio.filasieno@gmail.com wrote:
On Aug 28, 2007, at 12:30 AM, Bert Freudenberg wrote:
With the generic pipe object from my change-set in the original thread this gets you both - no need to define new methods:
highestNumberedChangeSet
"ChangeSorter highestNumberedChangeSet" ^self allChangeSetNames asPipe select:[:aString | aString startsWithDigit]; collect:[:aString | aString initialIntegerOrNil]; ifNotEmpty:[:list | list max]
I have to admit that the asPipe idea is really cool and it might do the trick.
A minor minor thing: you still need to explain it, and it's better to have 1 syntax token attached to 1 meaning. Is a bit ugly that cascade changes it's meaning: sometimes does X, some times does Y.
Still the asPipe is a very very nice hack.
I think the question is different from adding or not syntax to do a trick.
Do Small-talkers want to assert:
"Use functional programming when possible"
or not ?
This is question. And I can't answer that.
It's not : "Shall we add a new syntax token to do the trick ?"
Fabio FIlasieno
PS. Note that I associate functional programming with a chained application of functions (better if pure)
On Aug 27, 2007, at 10:38 PM, Michael Lucas-Smith wrote:
I wish more of Smalltalk were written with this approach in mind, it'd scale without effort then.. and programmers wouldn't accidently create memory explosion bottlenecks without trying. Multiple select:, collect:, reject: calls on large data sets will bring any image to its knees in seconds if more than one concurrent user invokes the same sort of operation at once.
The speed issue comes not from the time it takes to do one call of this - but what happens when multiple processes try to do the same thing (eg: multiple users hitting your server at once). And the speed issue comes in not from computing CPU cycles, but from memory allocation and garbage collection.
If you start with a collection of 100,000 things, you do 4 operations on it - three collect:'s and a reject:.. you'll probably be allocating 4 arrays of 100,000 slots. That's 1.2mb's of data you've just allocated. Now get 10 users using the same function at the same time and you've just made 12mb's of data. Scale it up a little more elaborate chains of functions or more users and you have serious scalability issues.
That is not the point ! Performance is for LATER !
example:
obj proc: param | proc:param | proc:param
1) simplicity: no need to learn packages or libraries, a kid can do it easily too, and that for me is important ! Smalltalk is also for kids right ? 2) good for your thoughts: I usually do : write a method call (put a pipe). Think. write another method call (put a pipe). Think some more. write yet another method call .... aaaaaaand put a dot. Done.
Personally I start with long chains. Then I write some tests. The I rewrite after measuring. The rewrite is easy because you have a high level specification written in front of you ! Easy to read. Easy to understand. Which says to you:
do this -> then do that -> then do this -> ... and then you are done.
The previous line is a simple SPEC !!!! IF is required - by hard proof. measured stats !!!! - I'll change the long chain it to ...
doFirstPartQuick -> doSecondPartQuick.
I decided to go for Smalltalk for better prototyping not for speed. I spend usually 40% design 40 testing% 20%dumb coding. With Smalltalk my aspirations were to cut the first and second, which are big for me. I can't comment on current methods. Since I'm new on smalltalk. Not having the pipe slows me - at least me, but I think all of you functional programmers - on the design bit.
This is bad because prototyping is king here. The fastest the design bit, the better.
Small-talkers you are competing with: Directly with - Ruby - Python - Erlang Indirectly with - Ocaml - Haskell
Why should I choose Smalltalk ?
The Smalltalk community needs to understand what a non smalltalk users sees in smalltalk. I can tell you what attracted me to smalltalk - part from Alan Kay's videos. Smalltalk is like drawing with a pencil. Other languages feels like drawing with a CAD. You need to use the pencil before using the CAD. Since Pipes makes Smalltalk better at prototyping, Pipes gives me a sharper pencil.
Fabio Filasieno
On Aug 28, 2007, at 1:58 , Fabio Filasieno wrote:
Small-talkers you are competing with: Directly with
- Ruby
- Python
- Erlang
Indirectly with
- Ocaml
- Haskell
Why should I choose Smalltalk ?
The Smalltalk community needs to understand what a non smalltalk users sees in smalltalk.
But also, newcomers to Smalltalk need to understand that to Smalltalkers, the *language* part is actually not all that important. This is very different from other communities like the ones you list, which are in fact centered around their language. For us Smalltalkers it is the environment that counts most, not the syntax.
- Bert -
On 28-Aug-07, at 2:41 PM, Bert Freudenberg wrote:
But also, newcomers to Smalltalk need to understand that to Smalltalkers, the *language* part is actually not all that important. This is very different from other communities like the ones you list, which are in fact centered around their language. For us Smalltalkers it is the environment that counts most, not the syntax.
Well yes, but.
The environment is only possible because we have a rather good semantic model serviced via a good, clean, regular, intelligible syntax. And some smart people using it, of course.
tim -- tim Rowledge; tim@rowledge.org; http://www.rowledge.org/tim Useful random insult:- Hypnotized as a child and couldn't be woken.
But also, newcomers to Smalltalk need to understand that to Smalltalkers, the *language* part is actually not all that important. This is very different from other communities like the ones you list, which are in fact centered around their language. For us Smalltalkers it is the environment that counts most, not the syntax.
Smalltalk guys compare proudly their language with others.
It VERY important the less writable (verbose) but very readable syntax (Code is meant to be read and only incidentally to be executed philosophy) It's VERY important the homoiconic LISPish small kernel. ("Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away." :o)
... the *language* part is actually not all that important.
No it is. And you know it. It's very very important.
On Aug 28, 2007, at 11:50 PM, Bert Freudenberg wrote:
On Aug 28, 2007, at 4:11 , Fabio Filasieno wrote:
It's just that I see this great opportunity for smalltalk that with a little fix people like me would appreciate Smalltalk more. The messaging system allows me to blend perfectly the mixed bottom- up top-down approach that I use in system building, but to do the bottom-up properly... I need the pipe.
"Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away." (Antoine de Saint-Exupery)
I see that quote as a need for the pipe, as it will remove many complex parts in a system to replace them for simple parts, `taking` away, as Antoine would say, many ugly parts and replacing them more small and beautiful parts.
On Aug 29, 2007, at 1:56 AM, Igor Stasenko wrote:
Bloat you say? but if implemented differently it can improve performance and transform collection using single loop. Instead of: ^ (self select: selectBlock) collect: collectBlock or with pipe syntax: ^ self select: selectBlock | collect: collectBlock
That is not important ! Performance is not important in the beginning ! I want the freedom write slow software that is fast develop, reserving the right to improve speed after measuring. If the code you say is executed one or twice, even if I miss that optimization that is not a problem. And you can always fix it.
In software performance comes later!!!!!! How many times this mistake has been made and remade... I always try to remember myself that software is 40% design (writing code not *really knowing what you are doing*, 40% writing code to test code, 20% *just code* code ).
Check this out and read a how the pipe is central to each point. http://www.faqs.org/docs/artu/ch01s06.html In general is this good or bad software engineering ? This is where - I - come from.
But I guess I'll stop annoying you on this pipe thing, at the time being :o) , as Vassili already did some good stuff for me for free. Thanks Vassili.
But ... The pipe is crucial to support functional programming better. (It's easy to reason about problems functionally ) The pipe is crucial to take away what CAN be taken away (Antoine de Saint-Exupery) The pipe is crucial to replace complex parts with simple parts (If I can already do it, why write a new method) The pipe is crucial get functionality implemented for free. (do method A, do method B, get A | B for free) The pipe is crucial to attract more people to smalltalk (functional programmers, which by the way are good hackers) The pipe is crucial to reduce bloat. (select:thenCollect it's hilarious ...) The pipe is crucial to make developers more responsible ( do I really need this method? Or is just mental masturbation ... )
I'll try to appear smart too with another quote of another italian bloke
"It must be considered that there is nothing more difficult to carry out, nor more doubtful of success, nor more dangerous to handle, than to initiate a new order of things. For the reformer has enemies in all those who profit by the old order, and only lukewarm defenders in all those who would profit by the new order, this lukewarmness arising partly from fear of their adversaries, who have the laws in their favour; and partly from the incredulity of mankind, who do not truly believe in anything new until they have had actual experience of it."
Niccolò Macchiavelli
-- Fabio Filasieno
On Wed, 29 Aug 2007 00:49:22 -0700, Fabio Filasieno fabio.filasieno@gmail.com wrote:
... the *language* part is actually not all that important.
No it is. And you know it. It's very very important.
But it misses the point. When you talk about most languages, you're JUST talking about the language. That's all there is. Yeah, these days you also get a VM for a lot of languages but it's a separate beast.
You can't discuss, e.g., changing C's syntax and still be talking about C. You can discuss--well, discussions have been had about doing Etoys in Python. You could do all of Squeak in Python, probably, or another dynamic language (perhaps with some minor adjustments) and a remarkably large percentage of the discussion would not change.
I love the Smalltalk syntax and would put it up against any other language, but it's more important that you can change it if you don't.
Hello,
Since Pipes makes Smalltalk better at prototyping, Pipes gives me a sharper pencil.
I think I know a part of your frustration. When you are trying make the idea rendered into the first cut of code, you really don't want to move the cursor back in the characters you typed and insert some other stuff just to make it compile and do something.
Another approach to solve this problem is to make the editor smarter. I could imagine to have a some command-key sequence that puts open and close parenthesis around the *last* expression. The definition of the "last" expression may need a clarification but it almost sounds like "right before the last statement separater ($.)" would be good. With it, if you type:
aCollection select: [:each | each isSomething]
and then realize that you actually want to continue to write collect:, then you press a key combination and it enclose the above expression with $( and $). In this way, you don't have to change the language.
For this kind of stuff, having three different precedence in message sending is basically two too many. That is why the lisp-mode in Emacs shines more than any other modes. But for Smalltalk-80 syntax, it wouldn't be too bad.
-- Yoshiki
On Aug 30, 2007, at 1:52 , Yoshiki Ohshima wrote:
Hello,
Since Pipes makes Smalltalk better at prototyping, Pipes gives me a sharper pencil.
I think I know a part of your frustration. When you are trying make the idea rendered into the first cut of code, you really don't want to move the cursor back in the characters you typed and insert some other stuff just to make it compile and do something.
Another approach to solve this problem is to make the editor smarter. I could imagine to have a some command-key sequence that puts open and close parenthesis around the *last* expression. The definition of the "last" expression may need a clarification but it almost sounds like "right before the last statement separater ($.)" would be good. With it, if you type:
aCollection select: [:each | each isSomething]
and then realize that you actually want to continue to write collect:, then you press a key combination and it enclose the above expression with $( and $).
+1
- Bert -
On 30/08/2007, Yoshiki Ohshima yoshiki@squeakland.org wrote:
Hello,
Since Pipes makes Smalltalk better at prototyping, Pipes gives me a sharper pencil.
I think I know a part of your frustration. When you are trying make the idea rendered into the first cut of code, you really don't want to move the cursor back in the characters you typed and insert some other stuff just to make it compile and do something.
Another approach to solve this problem is to make the editor smarter. I could imagine to have a some command-key sequence that puts open and close parenthesis around the *last* expression. The definition of the "last" expression may need a clarification but it almost sounds like "right before the last statement separater ($.)" would be good. With it, if you type:
aCollection select: [:each | each isSomething]
and then realize that you actually want to continue to write collect:, then you press a key combination and it enclose the above expression with $( and $). In this way, you don't have to change the language.
For this kind of stuff, having three different precedence in message sending is basically two too many. That is why the lisp-mode in Emacs shines more than any other modes. But for Smalltalk-80 syntax, it wouldn't be too bad.
And then the only thing you need is to implant a chip into your brain to be able read code with many (...) fast :)
-- Yoshiki
Igor,
Another approach to solve this problem is to make the editor smarter. I could imagine to have a some command-key sequence that puts open and close parenthesis around the *last* expression. The definition of the "last" expression may need a clarification but it almost sounds like "right before the last statement separater ($.)" would be good. With it, if you type:
aCollection select: [:each | each isSomething]
and then realize that you actually want to continue to write collect:, then you press a key combination and it enclose the above expression with $( and $). In this way, you don't have to change the language.
For this kind of stuff, having three different precedence in message sending is basically two too many. That is why the lisp-mode in Emacs shines more than any other modes. But for Smalltalk-80 syntax, it wouldn't be too bad.
And then the only thing you need is to implant a chip into your brain to be able read code with many (...) fast :)
A proper indentation helps, of course.
Prof. Takeuchi, a Lisp guru and known as the inventor of the "tak" function, is also full of sense of humor. One of his jokes takes a form of dialog between a Lisp Master and a young apprentice of his:
Apprentice: Master, there are too many parenthesis in this language! Even my eyes hurt! How could you write a program in it?
Lisp Master: Huh? What parenthesis? ... Ah, I totally forgot about them.
-- Yoshiki
On 8/30/07, Igor Stasenko siguctua@gmail.com wrote:
On 30/08/2007, Yoshiki Ohshima yoshiki@squeakland.org wrote:
And then the only thing you need is to implant a chip into your brain to be able read code with many (...) fast :)
It's not so bad. It's just as in Lisp: You don't count parens, you read based on indentation.
squeak-dev@lists.squeakfoundation.org