為什么我喜歡Smalltalk?
英文原文:Why I love Smalltalk
C, C++, Python等,這些算是傳統的語言吧,我從這些語言上學會了基本的編程技術。這之后,又有四種語言,它們讓我學到了一些新的東西。這些語言改變了我思考的模式,雖然我從來沒有使用過它們,但它們都是絕對值得你學習一下的。它們是:
- Smalltalk
- Lisp
- Erlang
- Haskell
你也許還會把Prolog加入這個列表中,但我沒有學過Prolog。本文是關于Smalltalk這種語言的。
我的目的并不是教大家怎么使用Smalltalk,而是向你展示一些Smalltalk能做到、而其它語言做不到的一些事情(聲明:有些語言也能做到,它們都是Smalltalk的一些方言)。不用說,我需要向你先介紹一下這種語言的一些基本知識,之后我才能向你展示更有價值的東西,那么就開始吧,第一個程序:
1 + 1
很顯然,計算的結果是2。如果你想把它存到一個變量里,這樣做:
m := 1 + 1
句子都要以點號(句號)結尾,像這樣:
m := 1.m := m + 1
在Squeak(這是Smalltalk語言的一種版本實現)里,有一個對象叫做Transcript,你把消息發送給它,它能把消息顯示到屏幕上。它很像一個Log窗口。你要這樣去用它:
Transcript show: 'Hello world'
運行的效果會是這樣:
Smalltalk的這種語法非常的獨特。消息(message)——這在其它語言里也叫做“方法”——是show:
(包括冒號),它接受一個參數。我們用下面的寫法可以讓這個句子運行10遍:
現在你開始能看出Smalltalk的獨特之處了。我把消息timesRepeat:
發送到對象“10”——一個Integer
類。這N次的循環操作是由這個Integer
來執行的,你認真想想,其實很有道理。
第二個有趣的部分是代碼段落(block),是在方括號里面的部分。你可能認為它跟其他種語言里的代碼段落語法是同樣的道理,比如Java的:
但你要是從Smalltalk的視角來看,你會發現它強大的多。它實際上是個閉包(closure)。看這段:
現在,我有了一個叫做t
的變量,它的類型是BlockClosure
,通過這個變量,我可以做我想做的任何事情。如果我向它發送class
消息,它會返回它的class類型:
t class
如果我向它發送value
消息,它會運行,會在Transcript里留下“Hello World”字符:
t value
讓我們多看幾段程序。一個沒有任何參數的消息:
10 printString
帶有一個參數的消息:
10 printStringBase: 2
帶有兩個參數的消息:
10 printStringBase: 2 nDigits: 10
很可愛,不是嗎?這個方法叫做printStringBase:nDigits:
。我沒在其它地方見過這樣的語法;只有Objective-C是個例外,因為它是從Smalltalk承襲過來的。
小玩意已經說的不少了,現在說點復雜點兒的東西。我們來創建一個類:
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Pupeno'
注意,一個類的創建是通過向其它類發送消息 —— 包括名字和一些參數,告訴它我要繼承它。這是一個消息,跟其它類型的方法調用一樣。對象是類,類也是對象。Smalltalk的對象模式非常的優雅,但這是另外一個話題。
現在我們有了一個類,我們來創建一個方法,叫做greet:就在這個類里。
在方法定義里,首先我們給這個方法加了一個注釋,然后是管道 (“|”)包著的本地變量,然后是方法的實現,我把”Hello“放到了變量message里,然后用逗號符把它和變量name連接起來。然后我把它發送到Transcript里。
運行起來的結果像這樣:
好了,我們來用一用它:
m := MyClass new. m greet: 'Pupeno'
為了創建一個類MyClass
的對象,我們向這個類發送new
消息。這個new
并不是像Java里的關鍵字。new
是一個方法。你可以看它的源代碼,覆蓋它,等等。不要動它,除非你十分清楚你在做什么。
事實上,如果你想一下,你會發現我們沒有看到任何的關鍵字。看看我們寫過的這些代碼,沒有什么要記住的關鍵字!更重要的,目前為止,你已經基本的認識Smalltalk了。Smalltalk就是這些,但就像是一個小積木塊,這些小塊能讓你搭建出你想要的任何東西。
不錯,就這些,我要說的就這些。我們看到了,Smalltalk里沒有循環,它有整數類,這個類里實現了timesRepeat:
消息,可以用來把事情重復執行N次。像這樣用于循環操作的方法到處都是。
你會問,有沒有if
這個關鍵字?Smalltalk里肯定有一個if
關鍵字,不是嗎?不,沒有。你所謂的if
語法在Smalltalk里可以用你剛才看到的類和消息傳遞的機制實現。為了好玩,我們來實現一個。
我們從創建一個PBoolean
類開始,然后兩個繼承它的類 —— PTrue
和 PFalse
。
Object subclass: #PBoolean instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Pupeno' PBoolean subclass: #PTrue instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Pupeno' PBoolean subclass: #PFalse instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Pupeno'
我們之前創建了一個類,MyClass,我們要給它定義一個equals:
方法,它能返回true和false,也就是我們的PTrue
和 PFalse
。
equals: other ^ PTrue new
這個小帽子,^
,是返回的意思。我寫的是硬編碼讓它返回true。現在我們可以在程序來用它了:
m1 := MyClass new. m2 := MyClass new. m1 equals: m2
得到的是true。我們已經接近目標了,但還不是if
。if
應該是個什么樣子?它的樣子應該是這樣:
m1 := MyClass new. m2 := MyClass new. (m1 equals: m2) ifTrue: [ Transcript show: 'They are equal'; cr ] else: [ Transcript show: 'They are false'; cr ]
估計你在想,怎么才能實現這樣的效果。我在PTrue
里加入了一個方法:
ifTrue: do else: notdo ^ do value
這個方法看上去是接受2個參數,但執行時接受第一個,忽略第二個。對于PFalse
,正好相反:
ifTrue: notdo else: do ^ do value
這就可以了。一個可以用的if
!如果讓我說,我覺得這真的很神奇。如果你去檢查Squeak了的代碼,你會發現它里面的if
就是這樣實現的:
如果你使用的編程語言能允許你創建像if條件這樣的基本功能,那它就可以讓你創建任何你想要的東西。