Kort-kort om Smalltalk

1           Konstanter och litteraler

Vissa typer av objekt kan man, precis som i Java, skapa eller använda utan att explicit instansiera en klass.

1.1         Konstanta värden

I Smalltalk finns det tre konstanter true, false och nil. Där nil motsvarar Javas null. Dessa värden är singletons i klasserna True, False respektive UndefinedObject.

1.2         Litteraler

Vissa objekt kan också skapas direkt, dvs utan att man explicit behöver instansiera en klass.

·         Tecken, skrivs med ett dollartecken ($) framför, exempel

$x $2 $; $$

·         Sträng, skrivs inom ett par av strängparenteser (')

'Detta är en sträng'

'Smalltalk''s way of writing the string parenthesis within a string'

·         Tal

1 345616777187 2.5 6/7 6.023e23 123d

·         Symbol, skrivs med en brädhög (#) framför

#DettaÄrEnSymbol

·         Vektor, skrivs med en brädhög (#) följt av en "argumentlista" med vektorns önskade innehåll

#(1 2 3) #(a b c) #(1 $x 45 'sträng') #(1 $x 45 'sträng' #(1 2 3))

·         Meddelanden

I Smalltalk sker allting via meddelanden till objekt på formen:

                objekt meddelande

Dvs alla meddelanden har en mottagare. Det är mottagaren som avgör vilken metod som används.

-12 abs
(3@-4) abs
Object new
OrderedCollection new

1.3         Tre typer av meddelanden

I Smalltalk finns det tre typer av meddelanden. En typ utan argument, en med ett argument och slutligen en typ med ett eller flera argument.

1.3.1        Dom tre typerna av meddelanden

   unära (meddelanden utan argument skrivna med vanliga alfabetiska tecken)

Exempel:

12 sin
nil isNil
47.2 class
200 factorial

  binära (meddelanden med ett argument)

Ett binärt meddelande består av ett eller två tecken ur följande mängd av tecken ($ indikerar att det som följer är ett tecken):

$+ $/ $\ $* $~ $< $> $= $@ $% $| $& $? $! $,

Ett binärt meddelande kan ockå inledas med $-.

Exempel på legala meddelanden:

+ -   !   =    ?   !!   !=   ==   ++   -+   <?

Exempel:

3 + 5
'OOMPA-kursen', ' ger sex poäng. Välbetald kurs! Eller?'
2 = 7

   nyckelords (meddelanden med ett eller flera argument åtskilda med $: skrivna med vanliga alfabetiska tecken)

Exempel:

12 max: 19

5 between: 3 and: 7
Array with: 'ett' with: 2 with: #(a b c)

1.3.2        Notera

Då binära meddelanden är vanliga meddelanden kan man om man vill skriva sådana i egna metoder. Om man tex skriver en klass Matris så kan man i denna konstruera en metod *.

1.4         Preferensregler

Meddelanden utförs från vänster till höger, unära meddelanden först, sedan binära och sist nyckelordsmeddelanden. Ordningen kan ändras med hjälp av parenteser.

1.4.1        Unära meddelanden i sekvens

Resultatet av ett meddelande är ett objekt till vilket ett nytt meddelande kan skickas.

Object new class

200 factorial reciprocal sqrt

1.4.2        Att använda parenteser för att ändra meddelandesändningsordning

   5 between: (3 min: -4 abs) and: (-2 ** 3) negated

  5 between: (3 min: 4) and: -8 negated

  5 between: 3 and: 8

  true

1.4.3        Meddelandesändningsordningen vänster till höger gäller alltid!

I Smalltalk görs ingen skillnad på ordningen av meddelandesändningen beroende av de ingående objektens typ, utan grundregeln vänster till höger gäller alltid. Så även om dom ingående objekten är tal och meddelandena är aritmetiska operationer så gäller denna regel.

   2 + 3 * 4

  (2 + 3) * 4

  5 * 4

  20

Återigen; ordningen kan ändras med hjälp av parenteser:

   2 + (3 * 4)

  2 + 12

  14

2           Instansiering och variabler

Först några regler:

·         Ett objekt skapas oftast genom att meddelandet new skickas till önskad klass.

·         Tilldelning görs med :=.

·         Satsparenteser skrivs med en punkt (.). I Java motsvaras detta av semikolon (;).

·         En variabel kan antingen vara instans-, klass-, pool-, temporär- eller global variabel.

·         Lokala variabler skrivs med inledande gemen och globala med inledande versal.

·         Temporära variabler måste deklareras  genom att räkna upp dem inom två lodräta streck (|).

Ett exempel:

| collection x | "Två temporära variabler"

"Sätt collection att referera till en ny  OrderedCollection"

collection := OrderedCollection new.

 

"Lägg in aktuell tid i collection"

collection add: Time now. "Meddelandet now ger aktuell tid!"

x := 1.

collection add: x.

x := 'x blir sträng. Smalltalk är ju dynamiskt så det går bra!'.

collection add: x.

 

"Global variabel kan användas, fast bör helst undvikas"

GlobalDict := Dictionary new.

GlobalDict at: #myCollectionKey put: collection

3           Transcript-fönstret och kaskadmeddelanden

Det finns en global variabel Transcript som refererar till det så kallade Transcript-fönstret.

Det går att skriva ut strängar i  detta fönser genom att tex skicka meddelandet show: till Transcript. Vagnretur görs via meddelandet cr. För tabulering använd meddelandet tab. För ytterliggare information leta reda på den klass som Transcript är instans av (tex genom att unyttja meddelandet class och find class i Browsern)!

Om det objekt man önskar skriva ut i Transcript mha show: inte är en sträng så kan man först omvandla objektet till en sträng via meddelandet printString (definierad i Object, så alla objekt förstår detta meddelande).

Exempel:

| x y z|

x := y := 'Dubbel initiering'.

z := 7906277.

Transcript cr.

Transcript show: x.

Transcript cr.

Transcript show: y printString.

Transcript cr.

Transcript show: z printString.

I vissa situationer kan det vara arbetsamt eller ge onödigt mycket text att skriva i situationer liknande det ovan beskrivna exemplet. För varje show:  skickar vi också meddelandet cr. Då kan det vara bra att känna till så kallade kaskadmeddelanden.

Kaskadmeddelanden är helt enkelt vanliga meddelanden åtskilda med $; (semikolon). Mottagare av alla meddelanden i kaskaden är det objekt som tar emot det meddelande som står närmast före semikolonet.

Om vi tex vill skicka meddelande m följt av meddelandet n till objektet o så kan vi antingen göra som förut dvs:

o m.
o n

Eller använda kaskadtekniken vilket skulle ge:

o m; n

Vårt Transcript-exempel skulle kunna skrivas om med utnyttjande av kaskadmeddelande. Vilket resulterar i följande med överskådliga kod:

| x y z|

x := y := 'Dubbel initiering'.

z := 7906277.

Transcript cr; show: x.

Transcript cr; show: y printString.

Transcript cr; show: z printString.

4           Block, villkor och styrstrukturer

I Smalltalk är allt objekt och meddelanden. Så även block, villkor och styrstrukturer, som därför hanteras som vanliga objekt och med vanliga meddelanden. Därför finns också klasserna och metoderna som hanterar detta i Smalltalk-systemet. Använd gärna en Browser för att undersöka dessa klasser och metoder.

4.1         Block

Ett block anger en fördröjd evaluering. Motsvarar närmast ett lambdauttryck, som man tex hittar i Scheme, och i viss mån anonyma klasser i Java. Ett block tillhör klassen BlockClosure.

Block utan argument:

| block |

block := ['Skicka value till detta block så skrivs denna sträng som resultat'].

block value

 

| x block |

x := 1.

block := [x := x + 1].

block value.

block value

Block med ett argument:

| x block |

x := 1.

block := [:arg | x := x + arg].

block value: 3.

block value: 4

En (numerisk) deriveringsfunktion skriven mha block

| dx func |

dx := [:f :x :h | (f value: x + h) - (f value: x) / h].

func := [:x | x ** 3].

dx value: func value: 10 value: 1/10000

Vi kan enkelt återanvända och anropa dx med en annan function, x-värde och h:

dx value: [:x | (x ln) - (1/x) sin] value: 100 value: 0.0001d.

4.2         Villkor

Villkor utförs genom ett meddelande till en boolesk variabel. Beroende av variabeln/mottagaren är sann eller falsk utförs ett argumentblock. Implementationen av villkor (ifTrue:, ifFalse:, ifTrue:ifFalse:, mfl) hittas i den abstrakta klassen Boolean med subklasser True respektive False.

2 > 3 ifTrue: ['2 är större än tre!']

 

2 > 3

   ifTrue: ['2 är större än tre!']

   ifFalse: ['2 är inte större än tre!']

  

| pnr |

pnr := '123456-1234'.

(pnr at: pnr size - 1) digitValue odd

   ifTrue: ['En man']

   ifFalse: ['En kvinna']

4.3         Styrstrukturer/iteratorer

Styrstrukturer hittar man bla i klassen BlockClosure.

For-loop (som "startar" i klassen Number som i sin tur använder klassen Interval)

| sum |

sum := 0.

1 to: 10 do: [:i | sum := sum + i].

sum

While-loop

| sum i |

sum := 0.

i := 1.

[i <= 10]

   whileTrue: [sum := sum + i.

               i := i + 1].

sum

 

Vissa "iteratorer" hittar man i Collection-klasserna också.

   #(5 4 22 3 5 6 11 222 1 22 99 )

            do: [:x | Transcript show: x; cr]

 

        #(5 4 22 3 5 6 11 222 1 22 99 )

            collect: [:x | x squared]

 

        #(5 4 22 3 5 6 11 222 1 22 99 )

            select: [:x | x even]

 

        #(5 4 22 3 5 6 11 222 1 22 99 )

            asSortedCollection: [:x :y | x > y]

 

Den intresserade kan utforska fler av dessa med hjälp av Browsern.

5           Metod

Den kod som implementerar responsen på ett visst meddelande kallas för en metod.

5.1         Returvärde från metod

En metod returnerar alltid ett värde och om inte annat anges så returneras det objekt som är mottagare av meddelandet.

m

"Den här metoden returnerar self (implicit)"

Transcript show: 'Test'

Ekvivalent med:

m

Transcript show: 'Test'.

^self

5.2         Den speciella variabeln self

För att från en metod referera aktuellt objekt så används den speciella variabeln self. Vars användning vi såg exempel på ovan. Denna variabel motsvarar Java:s variabel this. En skillnad mot Java är att vid meddelandesändning till det aktuella objektet är inte self underförstått utan self måste alltid skrivas ut.

m

self n "Skicka meddelandet n till aktuell instans"

5.3         Returvärde annat än self

Som vi sett tidigare så returneras alltid någonting från en metod. Sägs inget annat returneras self. Vill vi returnera något annat så är det bara att använda uppåtpilen/taket (^) följt av det som skall returneras.

n

"Den här metoden returnerar olika strängar beroende på om klassnamnet för mottagaren är längre än fem tecken eller ej"

5 > self class name size

      ifTrue: [^'Lång']

      ifFalse: [^'Kort']

Förutom i slutet av ett block kan retursymbolen också skrivas innan sista satsen i metoden (och då returnera resultatet av denna sats).

n

^5 > self class name size

     ifTrue: ['Lång']

     ifFalse: ['Kort']

5.4         Implementation av olika typer av metoder

Som vi sett finns det tre typer av metoder i Smalltalk, nämligen unära-, binära- och nyckelordsmetoder.

5.4.1        Unär metod

Metoder utan argument, exempelvis metoden abs i klassen Integer

abs

"Answer a Number that is the absolute value (positive magnitude) of the receiver."

 

^ self < 0

     ifTrue: [0 - self]

     ifFalse: [self]

5.4.2        Binär metod

Metoder med precis ett argument och som använder tecken som inte är bokstäver eller siffror, exempelvis metoden <= i klassen Magnitude (som bla är superklass till Integer)

<= aMagnitude

"Answer whether the receiver is less than or equal to the argument."

 

^(self > aMagnitude) not

5.4.3        Nyckelordsmetod

En metod som skrivs som flera nyckelord med ett argument per nyckelord, exempelvis metoden between:and: i klassen Magnitude

between: min and: max

"Answer whether the receiver is less than or equal to the argument, max,and greater than or equal to the argument, min."

 

^self >= min and: [self <= max]

6           Klass

I Smalltalk, precis som i Java, finns det en klass Object som är rotklass till alla klasser i systemet. När en ny klass skapas så görs detta genom att subklassa redan existerande klass.

Observera att man normalt använder programmeringsverktygen och bara fyller i mallar för att skapa klasser och shared variables, fast man kan göra det med normal meddelandesändning också om man nu vill det.

6.1         Ny klass, VisualWorks version 3 och tidigare

Deklareras som subklass till redan existerande klass. Den nya klassen skapas genom meddelande till den existerande klassen där man anger den nya klassens namn på symbolisk form plus önskade instans- och klassvariabler samt en kategorisering (för att katalogisera klassen).

Instansvariabler deklareras åtskilda av mellanslag i en sträng efter nyckelordet instanceVariableNames:. På samma sätt deklareras klassvariabler efter nyckelordet classVariableNames:.

Object subclass: #MinNyaKlass

   instanceVariableNames: 'ivar1 ivar2 ivar3'

   classVariableNames: 'KlassVar1 KlassVar2'

   poolDictionaries: ''

   category: 'Lämplig kategorisering av klassen'

MinNyaKlass subclass: #MinNyaSubklass

   instanceVariableNames: 'ivar4 ivar5'

   classVariableNames: 'KlassVar3'

   poolDictionaries: ''

   category: 'Lämplig kategorisering av klassen'

Point subclass: #Point3D

   instanceVariableNames: 'z'

   classVariableNames: ''

   poolDictionaries: ''

   category: 'Mina 3D-hack'

6.2         Ny klass, VisualWorks version 5 och senare

I VisualWorks version 5 skapas subklasser vanligen som meddelenden till manrymder där man anger superklass som en av parametrarna. Klassvariabler heter shared variables och skapas på särskilt sätt.

6.2.1        Skapa ny klass

Om vi gör på samma sätt som i 7.1 får vi följande (man kan dock skriva Object istället för Core.Object).

Smalltalk defineClass: #MinNyaKlass

   superclass: #{Core.Object}

   indexedType: #none

   private: false

   instanceVariableNames: 'ivar1 ivar2 ivar3 '

   classInstanceVariableNames: ''

   imports: ''

   category: 'Lämplig kategorisering av klassen'

Smalltalk defineClass: #MinNyaSubklass

   superclass: #{Smalltalk.MinNyaKlass}

   indexedType: #none

   private: false

   instanceVariableNames: 'ivar4 ivar5 '

   classInstanceVariableNames: ''

   imports: ''

   category: 'Lämplig kategorisering av klassen'

Smalltalk defineClass: #Point3D

   superclass: #{Core.Point}

   indexedType: #none

   private: false

   instanceVariableNames: 'z '

   classInstanceVariableNames: ''

   imports: ''

   category: 'Mina 3D-hack'

Oftast skapar man en egen namnrymd först, tex Smalltalk.OOMPA.

Smalltalk defineNameSpace: #OOMPA

   private: false

   imports: '     

       private Smalltalk.*

       '

   category: 'Min namnrymd'

I så fall blir första klassdefintionen:

Smalltalk.OOMPA defineClass: #MinNyaKlass

   superclass: #{Core.Object}

   indexedType: #none

   private: false

   instanceVariableNames: 'ivar1 ivar2 ivar3 '

   classInstanceVariableNames: ''

   imports: ''

   category: 'Lämplig kategorisering av klassen'

Dom andra definieras analogt.

6.2.2        Skapa shared variable

Skapa en shared variable på ungefär följande sätt:

OOMPA.MinNyaKlass defineSharedVariable: #KlassVar1

   private: false

   constant: false

   category: 'my shared variables'

   initializer: ''

6.3         Klassbibliotek

Precis som Java har Smalltalk ett rikt klassbibliotek med flera tusen klasser. Den stora svårigheten när man lär sig Smalltalk är egentligen att få en känsla av vad som redan finns och lära sig orientera och hitta i klassbiblioteket för att vid behov undersöka detaljer och återanvända det som redan finns.

6.4         Jämförelse med Java

I Smalltalk är allt klasser så det går att i princip att undersöka allt från hur klassen Object ser ut, hur hel- och flyttal är implementerade, styrstrukturer, parser, kompilator, systemklasser och verktyg, avlusare, minneshantering, till hur klasserna själva är implementerade med super-, subklasser och metodkataloger. Detta gör Smalltalk öppnare än Java men samtidigt så blir ju klassbiblioteket rikare, vilket gör att man har en större mängd klasser att hantera.

7           Subklass

Subklass skapas genom meddelandesändning till redan existerande klass (se avsnitt 6 ovan) eller idag vanligare (version 5) meddelande till namnrymd med superklass som en av parametrarna.

7.1         Var påbörjas metoduppslagningen?

För att påbörja metoduppslagningen en nivå ovanför var aktuell metod är deklarerad används den speciella variabeln super.

7.1.1        Påbörja metoduppslagningen i mottagande objekts klass

MinSuperklass>>m

"med this börjar metoduppslagningen i mottagarens klass"

self n

7.1.2        Påbörja metoduppslagningen i superklass

MinSubklass>>m

"med super börja leta efter metod en nivå ovanför den klass där den aktuella metoden hör hemma, dvs en nivå ovanför MinSubklass"

super m

7.1.3        Jämfört med Java

Användningen av self respektive super vid meddelandesändning är alltså ekvivalent med Javas användning av this respektive super.

8           Konstruktorer

Smalltalk har inga speciella konstruktorer utan klasserna är vanliga objekt med metoder för att instansiera dem som inte heller behöver ha speciella namn som i tex Java (där dom alltid heter som klassen). Fast dom grundläggande new och basicNew finns definierade från början. Det finns inte heller några regler om att en eventuell superklass konstruktör måste anropas på viss plats i subklassens konstruktör. Vidare skapas inga "implicita" konstruktörer utan argument automatiskt. Det finns redan från början en explicit konstruktör utan argument, nedärvd från klassens superklass. Innan vi visar hur "konstruktörer" skapas så några diskuterar vi kort begreppet metaklass och dess roll i Smalltalk.

8.1         Metaklasser

I Smalltalk är också klasserna objekt som i sin tur tillhör klasser, s.k. metaklasser. Metoder för meddelanden som skall skickas till klassen skapas därför i metaklassen (dvs grovt metoder som motsvarar statiska metoder eller konstruktörer i Java).

Metaklasserna ingår i en egen klasshierarki med dom "vanliga" Smalltalkklasserna Behavior, ClassDescription och Class som superklasser.

Object ()

 

   Behavior ('superclass' 'methodDict' 'format' 'subclasses')

       ClassDescription ('instanceVariables' 'organization')

             Class ('name' 'classPool' 'environment')

                   ... all the Metaclasses ...

             Metaclass ('thisClass')

                   AbsentClassImporterMetaclass ('extraInstVars')

Metaklasserna har inget explicit eget namn utan brukar identifieras via den klass som är dess instans med tillägget klass. Så exempelvis heter klassen Object:s klass Object class och klassen Point:s klass Point class. Alla metaklasser är instanser av klassen Metaclass som i sin tur är instans av klassen Metaclass class som är instans av klassen Metaclass….(hänger ni med?!).

Smalltalkklasserna Behavior, ClassDescription och Class definierar själva klassernas struktur med super- och subklasser, instansvariabler, metoder osv.

8.2         Metoder för att skapa instanser av klasser

I Smalltalk, där ju klasserna också är objekt som tillhör klasser (s.k. metaklasser), ser metoderna för att skapa instanser ut som vilka andra metoder som helst. Konstruktorer i tex Java-mening, som ju måste heta samma sak som klassen, finns egentligen inte. Det vanligaste sättet att skapa en instans av en klass är genom att skicka meddelandet new till klassen.

Object new

Metoden new kan omdefinieras i subklasser (hypotetisk klass).

MyStack class>>new

"Anropa superklassens new och skicka meddelandet initialize till den nya instansen som då skapats"

^super new initialize

Föregående exampel skulle också kunna se ut så här (där vi "mellanlagrar" den nya instansen i en temporär variabel):

MyStack class>>new

| newStack |

newStack := super new.

newStack initialize

^newStack

En konstruktör kan också ta ett annat namn än new, men på något sätt brukar new alltid anropas.

Light class>>on

^self new isOn: true; yourself

Ibland kan en superklass förhindra instansiering via new eller alternativt vill man själv hindra instansiering i den egna klassen (för att tex tvinga fram instansiering med givande av argument):

Person class>>new

self shouldNotImplement

Då kan man istället använda basicNew där det behövs:

Person class>>name: aName email: anEmail socSecNo: aString

"Instansiera och skicka instansmeddelandet name:email:socSecNo: till denna nya instans"

^self basicNew

     name: aName

     email: anEmail

     socSecNo: aString

I det här fallet hade vi också kunnat skriva:

Person class>>name: aName email: anEmail socSecNo: aString

^super new

     name: aName

     email: anEmail

     socSecNo: aString

Varför?

9           Namnrymder

Från och med VisualWorks 5 har namnrymder införts (även om dom i någon mening fanns tidigare). En namnrymd i Smalltalk är i stort sett ekvivalen med ett package i Java.

10       Namnkonventioner

Klasser, globala variabler, klassvariabler och poolvariabler inleds med en versal bokstav. Metoder, instansvariabler och temporära variabler inleds med en gemen bokstav.

11       Mer information

Det finns en hel del information, både som kommer med systemet och på nätet.

11.1     Föreläsningsanteckningarna

Beskriver bland annat hur du startar.

11.2     Inne i VisualWorks

Följande information finns on-line inne i systemet

Lite allmän information och referenser till mer information.

Hur kommer jag igång , vad består systemet av och var hittar jag mer info.

Läs denna allmänna introduction till framförallt språket Smalltalk.

Några sakeer man bör tänka på vid installation.

Information om hur jag kan skriva kod som tar reda på saker om systemet, var implementeras en viss metod, vilka skickar ett visst meddelande, hur många instanser finns av klass X, hur mycket minne finns kvar i en viss minneszon, hur snabbt exekverar ett visst kodavsnitt, osv, osv.

En intro till VisualWorks\Smalltalk.

Ladda in Smalltalk-hjälpen via Launchern.

11.3     Tutorial

Finns på nätet, se tex http://www.cincom.com/scripts/smalltalk.exe/education/index.asp?content=tutorials

som består av

·         Introduktion till VisualWorks

http://www.cincom.com/smalltalk/tutorial/home.htm

·         Mer om GUI

http://www.cincom.com/smalltalk/tutorial2/home2.htm

 

11.4     Dokumentation

Finns i katalogen doc. På NADA hittar du denna katalog i VisualWorkskatalogen /pkg/visual/5i.4nc/.

Jag har lagt upp en länk till detta också:  http://www.nada.kth.se/~bjorne/vw5doc/.

Titta speciellt på dom feta röda.