A Factor programozási nyelv

Alprogramok, modulok

Új szavak deklarációja

Factorban az alapvető kódstrukturálási eszköz az új szavak bevezetése, amihez meg kell adnunk egy kettőspont operátor után az szó nevét, stack-effektjét és pontosvessző szóval lezárt törzsét.
Példák saját szavakra:

: skip ( -- ) ; ! A skip szó így definiálható skip ! Eredmény: semmi se történik! : printhello ( -- ) "Hello World!" print ; printhello ! Eredmény: "Hello World!" : sq ( x -- y ) dup * ; 4 sq . ! Eredmény: 16 (=4^2) : myabs ( x -- y ) { { [ dup 0 < ] [ -1 * ] } [ ] ! Ez a sor nélkül exception-t kapunk minden nemnegatívra! } cond ; -42 myabs . ! Eredmény: 42 : myif1 ( ..a ? true: ( ..a -- ..b ) false: ( ..a -- ..b ) -- ..b ) ? call ; inline ! myif1 csak inline értékekre jó t [ "u9" ] [ 0 ] myif1 . ! Eredmény: "u9" : myif2 ( a b c d -- e ) ? call( x -- y ) ; ! myif2 csak ( x -- y ) kódidézetre jó ! : myabs2 ( x -- y ) dup 0 < [ -1 * ] [ 1 * ] myif2 ! myabs2 kódidézetei megfelelnek myif2-nek ! -42 myabs2 . ! Eredmény: 42

Látszik, hogy az alapvető szintaktika nem bonyolult, csak ha valami furcsaságot akarunk csinálni, amikor is néha meg kell szenvednünk az inline szavak beirogatása és a bonyolultabb stack effektek miatt. A myif1 szavunk stack effektje a következőképpen olvasható ki: "a verem általunk látott alja valami a-ra végződő dolog, amire rá van téve egy logikai érték, amire rá van téve egy true nevű kódidézet ( ..a -- ..b ) stack effekttel és egy false nevű kódidézet ( ..a -- ..b ) stack effekttel, az eredményünk pedig a stack korábbi tartalma és az a helyett valami b" - elsőre kicsit bonyolultnak tűnhet, de egyébként elég intuitív és ennél durvábbal nemigen fogunk találkozni. Ami itt még új elem az a inline szó, ami a legutolsó definiált szót inlineosítja és a "call(" szó, amelynek a fenti formában a legközelebbi ")" jelig meg lehet adni, hogy milyen stack effekttel rendelkező kódidézetet futtatunk és így elkerülhetjük a myif1 kacifántos deklarációjának szükségességét (vö. myif2-vel)!

Paraméterátadás

A szavak felfoghatóak függvényeknek is, melyek a stacken dolgoznak és a visszatérési értékeiket is arra írják vissza, illetve amennyiben csak használják a paramétereket, akkor felfoghatóak szubrutinoknak. A stack fizikai megvalósítás szintjén általában referenciákat tartalmaz az objektumokra, melyeket viszont értékké optimalizálhat bármikor a fordító a szemantikai jellemzők megváltoztatása nélkül (vagyis a lefordított kódban). Lehetnek szavak, melyek megváltoztathatják a paraméterliteráljuk értékét (mellékhatást keltve), ilyenkor a literálokat először clone művelettel klónozni kell és a másolatot adni át, ha nem akarjuk, hogy az eredeti objektum, melyre a referenciáink mutatnak megváltozzon. Ez egy ritka, de fontos megemlítendő esemény, mert ha egy szó megváltoztat egy nem mutable literál, akkor a programunk a legtöbb helyen, ahol ezt a literált felhasználjuk már nem úgy fog működni, amire számítanánk!

Rekurzió

A Factor nyelvben a szavakat definiálhatjuk rekurzívan is, sőt akár szavak közötti kölcsönös rekurzíval is, de ez utóbbi esetben egy problémába ütközhetünk, ugyanis nem hivatkozhatunk egy szóra, mielőtt definiáltuk, így látszólag nem lehetséges a kölcsönös rekurzió. Az előzőleg felvetett problémára a megoldás a "DEFER:" szó, mely előre létrehoz egy szó-szimbólumot, melynek hívása a tényleges definíció előtt hibát vált ki.
Példák:

: push-is-zero ( x -- x ? ) dup 0 = ; : push-is-one ( x -- x ? ) dup 1 = ; : dupdec ( x -- x y ) dup 1 - ; : factorial ( x -- y ) push-is-zero [ 1 ] [ push-is-one [ 1 ] [ dupdec factorial ] if ] if * ; 5 factorial . ! Eredmény: 120 (rekurzív faktoriális számítás) DEFER: foe : fie ... foe ... ; : foe ... fie ... ; ! Ilyen alakú kölcsönös rekurzió esetére kell a DEFER:

Lexikális és egyéb Változók

Factorban lehetőségünk van a stack-alapú működéstől elrugaszkodni és nevesített változókat használni a szavaink (vagy névtelen kódidézeteink) deklarációjának a törzsében, lexikális változók segítségével, amelyek sok esetben átláthatóbbá teszik a kódot, viszont a valóságban verem-alapú utasításokkal helyettesítődnek. Ha lexikális változókat akarunk használni, akkor szódefinícióknál egy : helyett kettőt használjunk (::), kódidézeteknél nyitó [ helyett [| szerkezetet, ha pedig a változóinknak egy scope-ot szeretnénk létrehozni, akkor használjuk a [let szerkezetet és írjuk a kódunkat a következő ]-ig bezárólag!
Példák:

USING: locals math math.functions kernel ; IN: scratchpad :: quadratic-roots ( a b c -- x y ) b sq 4 a c * * - sqrt :> disc b neg disc [ + ] [ - ] 2bi [ 2 a * / ] bi@ ; 1.0 1.0 -6.0 quadratic-roots [ . ] bi@ ! Eredmény: 2.0 -3.0 USING: locals math math.functions kernel ; IN: scratchpad [let 1.0 :> a 1.0 :> b -6.0 :> c b sq 4 a c * * - sqrt :> disc b neg disc [ + ] [ - ] 2bi [ 2 a * / ] bi@ ] [ . ] bi@ ! Eredmény: 2.0 -3.0 USING: kernel locals math prettyprint ; IN: scratchpad 5 3 [| m n | m n - ] call . ! Eredmény: 2

Ha tehát ezeket a szerkezetetek használjuk, akkor a stackről származó változókra névvel hivatkozhatunk egészen a szerkezet lezárásáig (; vagy ]) meglévő scope-al és további lokális változókat hozhatunk létre a :> jel felhasználásával. A fenti példában lévő bi@ művelet egy bonyolultabb kombinátor, használatához a stack-en két objektumnak és egy kódidézetnek kell lennie. A szó meghívja a kódidézetet mindkét objektumparaméterére. Ehhez hasonló a 2bi művelet is, csak ennél két kódidézet is van, melyeket egymás után alkalmaz az objektumparamétereken.

Egyéb változók a factor nyelvben:

Megemlítés szintjén jelezzük, hogy a nyelvben lehetőségünk van globális és dinamikus változók kezelésére is. Ritkán használják őket, egy lehetséges alkalmazási területük például a random szó megvalósítása, ami ezek nélkül az eszközök nélkül nehézkes lenne. A globális változók kezelését a set-global és get-global szavak segítségével oldhatjuk meg, a dinamukos változók kezelését pedig a get és set szavakkal. Utóbbiak esetén szabályozhatjuk a láthatóságot a namespace szótár lehetőségei szerint.
Rövid példák egyéb változóhasználatra:

SYMBOL: adsf ! A változóra bármilyen objektummal hivatkozhatnánk, SYMBOL: i ! de konvenció szerint szimbólumokkal hivatkozandóak ! 5 asdf set ! Dinamikus névteres változó beállítása ! (alapból scope: saját szótárunk) "i" i set-global ! globális változó értékének beállítása asdf get . ! dinamikus változó értékének lekérdezése (és kiírása) i get-global . ! globális változó értékének lekérdezése (és kiírása) 42 42 set-global ! A 42 számobjektum-hivatkozású változó értéke 42 legyen 42 get-global . ! Eredmény: 42 ! (melyet a 42 objektumhoz rendelt változóból nyerünk) ! ^^ez utóbbi antipatternt ne használjuk ha lehet!

Szótárak(vocabulary)

A Factor nyelvben a modularizációt a szótárak segítségével oldják meg. Ha a bevezető fejezetben lévő bemutató programot végigcsináltuk, akkor már mi magunk is készítettünk saját, USE: szóval behozható szótárat, mely külön fájlban található és "IN: szótárnév" vezeti be a fájl elején, közvetlenül a USING: után, hogy egy szótárat írunk.
A USE: és USING: szavakon kívül sok másik szó is létezik a vocabulary/szótár-kezelésre, például az UNUSE:-al elfeledkezhetünk arról, hogy használjuk a szótárat és vannak szavak melyekkel szótárnévvel minősítetten hozhatunk be szavakat egy vocabulary-ból(QUALIFIED: és QUALIFIED-WITH:), illetve olyanok is vannak, mint az EXCLUDE: szó, ami behozza az adott szótár szavait, kivéve pár általunk megadottat.