Ruby tervezési hiba?
A Ruby nyelv egyik legszebb vonása az, hogy minden nyelvi konstrukció rendelkezik értékkel. Nem csak a hagyományos kifejezések helyettesíthetők be egyetlen objektummal, de más nyelvekben szokatlan módon a vezérlési szerkezetek is.
Ez a gyakorlatban az adott szerkezetben utoljára végrehajtott utasítás értékének visszaadását jelenti.
Ennek köszönhetően megtehetjük például, hogy egy feltételt jobbértékként kezelünk:Hasonló filozófia érvényesül a függvények visszatérési értékénél is, minek köszönhetően egy kilvasófüggvény nem több, mint:Kényelmesnek tűnik, azonban elég súlyos biztonsági kockázatot rejt. Vegyük az alábbi metódust:Amennyiben az
Részemről ezt elég súlyos tervezési hibának érzem, mert könnyű megfeledkezni az explicit megadandó
Ráadásul, míg a vezérlési szerkezeteknél ez a lehetőség jól olvasható kódot eredményez, úgy egy függvénydefinícióban véleményem szerint a
■ Ez a gyakorlatban az adott szerkezetben utoljára végrehajtott utasítás értékének visszaadását jelenti.
Ennek köszönhetően megtehetjük például, hogy egy feltételt jobbértékként kezelünk:
permission_level = if session.admin? then
2
else if session.logged_in? then
1
else
0
end
class User
def admin?
@admin
end
end
class User
def delete
@##kukac##db.execute 'DELETE FROM users WHERE id = ?', @id
end
end
execute
tagfüggvény visszatérési értéke self
(mondjuk hogy láncolható legyen), vagyis az objektum, úgy ez lesz a delete
visszatérési értéke is, osztályunk felhasználója pedig máris szabadon hozzáfér az adatbázishoz: ez az adatrejtés vége.Részemről ezt elég súlyos tervezési hibának érzem, mert könnyű megfeledkezni az explicit megadandó
nil
értékről. És még ha az ember észben is tartja, fárasztó beirkálni és hülyén is hat a végén: elvégre ha a semmit adom vissza, azt minek kiírni? Így most ahelyett, hogy amikor kell, akkor return érték
-et írnék, amikor nem kell, írom a return nil
-t.Ráadásul, míg a vezérlési szerkezeteknél ez a lehetőség jól olvasható kódot eredményez, úgy egy függvénydefinícióban véleményem szerint a
return
elhagyása kifejezetten ront az értelmen.
Ez funkcionális
Mindazonáltal elég ügyetlen megközelítés, hogy egy számodra szokatlan viselkedésből ilyen messzemenő következtetéseket vonsz le. Azért kényelmetlen neked
return nil
-nel zárni a függvényeket, mert nem FP-ben gondolkozol és kódolsz, különben úgy írnád a függvényeidet, hogy azoknak legyen visszatérési értéke. Amint eléred ezt, üdvözlöd majd, hogy nincs szükség mindig azt explicit definiálnod.FP vs. OOP
Ezt a kérdést nem is értem
Ugyanazért, amiért a nullát is, mint számot. A "semmi" egy speciális "valami". Vezérlési szempontból a "semmi" ugyanúgy érték, mint bármi más. Az, hogy ezt egyes nyelvek kényelmesen átlépik, nem jelenti azt, hogy ez lenne a logikus út. A PHP-ban például minden függvény és eljárás null-t ad vissza ha nincs visszatérés meghatározva. Én ezt nem tartom helyesnek. Még egy, a típusokat annyira lazán kezelő nyelvben sem, mint a PHP. Ha az ember odafigyelve (tervezve) fejleszt, amúgy sem fordulhat elő, hogy egy függvény ne vezérelten térjen vissza értékkel, vagy hogy egy eljárás néha adjon visszatérési értéket, néha meg ne. A null, mint alapértelmezett visszatérése minden eljárásnak és függvénynek viszont egy hasznavehetetlen dolog, ami valószínűleg változna, ha a PHP valaha az életbe kinőné a kőkorszakot - inkább akkor pedig adja vissza $this-t vagy self-et, mert a null-al kitörölhetem (bocsánat).
Nekem meg ez nem lenne logikus
(Természetesen azzal egyetértek, hogy a "semmi" az fontos visszatérési érték -- de tervezni kell vele, és nem csak "úgy", és ez igaz bármi más visszatérési értékre)
Te határoztad meg
De, te határoztad meg, hogy fejlesztési környezetként a Rubyt választod, az adott függvényben pedig utolsó kifejezésként a
delete
függvényt hívtad meg, ami (Ceriak írása szerint, és egy Rubyt ismerő programozó számára tudvalevőleg)self
-fel tér vissza.Ennek az egész posztnak nem sok értelmét látom. Előbb ismerjünk meg egy eszközt, és amennyiben a módszertana, tervezése megfelelő számunkra, utána kezdjük el használni.
Nem a Rubyról beszéltem...
A poszt értelme
Szerintem rögtön a rosszat feltételezed. Én kifejezetten szeretem a Ruby-t, nem lehúzni akartam, pusztán találtam benne egy tervezési sajátosságot, ami hibának tűnt, és magamat nem tudtam meggyőzni az ellenkezőjéről. Az egyik hozzászólónak azonban sikerült, de erről majd ott.
Ha egyszer feltehetően
Én csak azt mondtam, hogy a null-nál a self vagy a $this OO szempontból praktikusabb alapértelmezett visszatérési érték lenne, ahogy nem OO eljárások esetén inkább a false-t tartanám szerencsésnek ugyanerre. Ennek belátásához szerintem kell kb 3 év PHP-s tapasztalat OO és nem OO szempontból, de ez mind csak a saját szubjektív véleményem.
Sőt - ahogy erre fentebb is utaltam - ezt az egész marhaságot, hogy eljárásnak visszatérési értéke lehet random fejlesztői hülyeség függvényében, eleve nem támogatom. A függvénynek legyen kötelező visszatérnie meghatározottan és minden esetben, az eljárás pedig ne térhessen vissza semmivel, mert onnantól nem eljárás. Ezért tartom kreténségnek, hogy nincs a PHP-ban szétválasztva egymástól ez a két fogalom a nyelv szintjén. (bár ellenérvként ezen álláspontom ellen szól, hogy ettől se lenne sokkal kevesebb figyelmetlen PHP fejlesztő a világban)
Tehát ha már az utóbbi szigor nem a PHP útja, akkor legalább olyan alapértelmezett visszatérési értékeket használhatna, aminek van hasznossága.
Igazad van
De a többivel egyetértek. (A kiscicán kívül, mert az sem lenne logikus :-)
False
A
false
egy logikai érték, tehát érték, és nem az érték hiánya, kifejezetten rossz választás volna implicit visszatérési értékként!(Képzeld csak el, mikor a nem létező tömbindexet lekérdezve
false
-t kapnál vissza: ezt most akkor beletette valaki, vagy nincs is ott?)Adjon vagy ne adjon
A PHP
null
-ja, a Rubynil
-je mind arra valók, hogy ahol szükséges, ott tudjam jelezni a semmit. Miért kéne külön jeleznem ott, ahol nincs rá szükség?Ha egy művelet nem ad vissza értéket, akkor arra szoktuk azt mondani, hogy
null
-al tér vissza. És miért ne volna létjogosultsága annak, hogy egy művelet ne adjon vissza semmit, ha csak egy objektum állapotát hivatott megváltoztatni. Vagy miért ne volna lehetséges, hogy egyszer adjon visszatérési értéket, egyszer meg ne. Hisz ha pl. olyan kulccsal kérek elemet egy gyűjteményből, amivel nem szerepel benne semmi, akkor egyértelmű, hogy semmit nem kapok vissza.Azt mondjuk járható útnak tartanám, ha az alapértelmezés
$this
avagyself
lenne, legalábbis egy OOP nyelvben, a bejegyzés lényegét ez nem érinti.A Ruby nem objektum, hanem
Köszönöm
Ez viszont felvet egy másik, engem régóta foglalkoztató kérdést: ha a Ruby olyannyira objektumorientált szemléletű, hogy nem is enged nyilvános adattagokat létrehozni, akkor miért engedi, hogy az utólag definiált tagfüggvények hozzáférjenek a változóihoz? Ezzel a megoldással két szék közé a földre esik: egyszerre próbálja az adatrejtést megkövetelő keményvonalas objektumorientáltságot, és a más prototípusos nyelvekben (JavaScript, Self) meglévő bővíthetőséget megvalósítani, de az előbbit csak felületesen valósítja meg, a másikat pedig megnehezíti.
És ezek szerint a JavaScript a
closure
adta lehetőségekkel erősebb adatrejtésben, mint a Ruby?Csak filozofálok, de akit érdekel, és jobban tudja, az ne habozzon ismét megcáfolni.
Csak filozofálok
Hiszen a programozó számára a forráskód rendelkezésre áll: ha úgy dönt, hogy eleve az osztályba tesz egy settert, hát megteszi: akkor már miért ne tehetné meg ezt utólag, prototype alapon?
Az utólag definiált függvény általi hozzáférés mindkettőnek megfelel: egyrészt lehetővé teszi, hogy saját magunk szabályai szerint, terv szerint hajtsuk végre az adathozzáférést, másrészt az "adatrejtés" érvényesül, ha nem definiálunk ilyen függvényt.
(Ebből az következik, hogy ha tényleg így van, akkor az eredeti feltevésed helyesnek látszik, vagyis ha a visszaadott objektumon keresztül "illegálisan" férünk hozzá adathoz, akkor az előre tervezettség védelme bukik, nem elég erős a koncepció)
Forráskód
Mi a hétköznapok során így dolgozunk, de azért ez koránt sem egyértelmű. Ugye a fordított nyelveknél egyértelmű a dolog, de a jövőben szinte biztos, hogy interpretáltaknál is fogunk úgy dolgozni, hogy készen kapunk objektumokat, mi, mint harmadik fél. (Javaban több próbálkozás is volt/van operációs rendszer létrehozására, ott tudtommal erősen építenek a nyelvi elérésszabályozásra. De mintha Crockford is a mashupok kapcsán üdvözölné a könyvében a closure-t.)
De akkor is
Konvenció vagy kényszer
Teszemazt a Windows van annyira meghatározó platform, mint a web, egyelőre jobban is, és ott is csak egy API-t kapsz. Amit ott rád kényszerítenek, az ellen nincs apelláta, legfeljebb biztonsági rés.
Én szeretek a jövőben gondolkodni, és szerintem az elkövetkező évtizedekben nagyságrendileg fog eltolódni a munkánk a készen kapott objektumokkal harmadik félként való munka irányába, még akkor is, ha ezek történetesen interpretált nyelv objektumai.
De vonatkoztassunk kicsit el a klasszikus programozástól, hogy egy jelenbeli példán át is megszemlélhessük a problémát: meddig volna működőképes a webszolgáltatások mai ökoszisztémája, ha az általuk nyújtott API csak egy konvenció volna, amit bármikor meg tudnánk kerülni, és mondjuk direktben túrni a Facebook adatbázisát?
Adat-adat, művelet-művelet, API-API, itt is ott is. Csak még gyerekcipőben járunk, de meg fog ez ám komolyodni.
ha a Ruby olyannyira
Szerintem a magyarázat ott is van az idézett részben: szemlélet. És nem paranoia.
Csak programozási stílust szorgalmaz, nem pedig rejtegetni segít.
Megjegyzés, hogy az Object.freeze metódus segítségével megakadályozható az osztály későbbi változtatása, tehát újabb metódusok deklarálása.