Gyengén típusos nyelvek

Eddig csak erősen típusos nyelvekben programoztam komolyabb szinten. Egyetemi beadandók miatt néha használtam gyengén típusos nyelveket is. Kicsit szeretném megismerni ezt a szemléletet is, hogy ezekben pontosan mi is a jó, mert egyelőre sajnos nem látom.

Azt a részét értem, hogy mondjuk van egy számom és az mindegy, hogy short, int, double, float és csak számként kezelem és ez nekem jó. Amikor kisebb programokat írtam ilyen nyelveken (php, js, ruby), akkor én igazából típusosan programoztam, mert figyeltem a visszatérési értékekre stb. (hiába nem kell azokat megadni a függvény/metódus fejlécében) Amennyiben ettől elrugaszkodunk és mondjuk Ruby-ben egy metódus Stringet, máskor valamilyen számot, megint máskor meg mondjuk egy File objektumot ad vissza egy paramétertől függően, akkor olyan kódot írtunk, amit erősen típusos nyelvben nem tudunk. (Tudunk ha Object a visszatérési érték, de ez most mindegy.) Ekkor viszont az a probléma áll fent, hogy visszakapunk egy objektumot, amiről nem tudjuk, hogy milyen metódusait hívhatjuk meg, mert elvileg nem tudjuk a típusát.

Nem flame topikot akarok, hanem tényleg megérteni ezeknek a nyelveknek a lényegét, erejét.

Hozzászólások

Arra jó, amit leírtál. Ha kisebb programot írsz (relatíve), akkor nem szeretnél törődni azzal, hogy az short, int, double, float, etc. legyen szám és kész, törődjön vele a compiler. Így más, fontosabb dolgok felé fordíthatod a figyelmed.
Viszont;
Az már gyengén típusos nyelveknél sem előny, ha egy metódus össze-vissza ad vissza értékeket; az ott tervezési hiba.

Én úgy látom, hogy kisebb problémákra sokkal egyszerűbben használhatóak, mert elfednek előled egy réteget.

Azert nem mindig hatrany, pl. ha a bemeneti tipust adja vissza, es leszarja, mi a bemeneti tipus, meg akkor is, ha belenez (pl. hogy tomb-e), vagy ha FALSE-t ad vissza bizonyos esetekben (az exception kezeles nem a vilag legkenyelmesebb megoldasa)

Alapvetoen nincs baj a gyenge tipusossagu nyelvekkel, a C# legujabb feature-ei (LINQ, sot, a kovetkezo C# 4.0 jo resze) a gyenge tipusossagra epulnek.

A szar programozot nem az eros tipusossag fogja megmenteni a szar programok irasatol, a jo programozonak meg mindegy, hogy erosen vagy gyengen tipusos-e a nyelv, lattam en mar tipusellenorzott assszociativ tombot.

Igen tudom, hogy nem előny ha össze-vissza ad vissza típusokat. Viszont ha nem ad semmi extra dolgot, olyat amit erősen típusos nyelvekben nem lehet, akkor sokkal több hátrányt ad. Pl. IDE is kevesebbet tud segíteni, mert nehezebb kitalálnia mondjuk egy metódus visszatérési értékét. Ezen kívül rengeteg hiba futási időbe jön elő és nem fordítási időben.

Gondolom egyebkent javas iranybol jottel, ok szoktak ilyen kerdeseket feltenni (mivel ezen kivul kb. minden modern nyelv gyengen tipusos, vagy konnyeden engedi ezt, a C++-t is elobb birod ra erre, mint a javat)

Azt kell megertened, hogy a tipusossag elsosorban biztonsagi korlat. Nyilvan segit abban, hogy ne ess le. Ugyanakkor nagyon gyakran akadalyozo tenyezo (Generics elotti java kodok pl.).

Gondold meg, ha a parameterekbol kivennenk a tipusellenorzest, egy helyes program tovabbra is jol futna.

A javaban nagyon sokszor csinaltak anno azt, hogy valamit atkuldtek Objectkent, majd a masik oldalon ezen tuli feltetelezeseket tamasztottak rola, pl. az osszes container (amibol volt par).

Ezen kivul biztos lattal mar rossz java kodot. Minden nyelven lehet rosszul programozni, nyilvan a javaban is lehet, es lehet, ha gyengebb lenne a tipusossag (bar valojaban a javaban baszottulkeves tipus van, de ezt most hagyjuk inkabb), akkor lehet, hogy tipus-alapu hibabol is sok lenne; a tapasztalat azonban azt mutatja hogy rendkivul keves gyakorlati problema jon elo, ha megszunik a szigoru tipusellenorzes (mert meg a rossz programozok is tudjak, mit varnak egy valtozo tartalmakent, igy nem irnak hulyeseget. Nyilvan elvi sikon irhatnanak, gyakorlatban nem szoktak.)

Azt is eszre kene venned, bar ebbol nem akarok flame-et, hogy a javaban az eros tipusossag miatt neha kortancot kell jarni, a legegyszerubb pelda erre a nevtelen osztalyok (bar ez leginkabb a fuggveny mint tipus hianyabol kovetkezik).

Ha ugy nezed, hogy a sokmilliard sornyi gyengen tipusos programban amit irtak eddig, es a sokszazezer-millio sornyiban, amit epp ma irnak, nincs ezzel kapcsolatos hiba, akkor elgondolkozol azon, szukseges-e az a biztonsagi korlat, ami azert neha kenyelmetlen.

En tovabbra is tartom, hogy a java kozosseg elmozdulasi iranyai - Scala, Groovy, Clojure - a tipusfuggetlen nyelvek fele visznek.

Szoval szerintem nem szuksegesek, es jol elvagyok a dinamikus nyelvekkel.

Ruby esetén látom ezt nagyon jól elsülni. A Ruby valódi OOP nyelv, ahol csak objektumok léteznek és üzenetek. Az objektum vagy elfogadja az üzenetet, vagy kivételt dob. Ez azért előnyös, mert ha egy függvény vár egy objektumot, akkor nem kell típussal megszorítani (aminek nem is lenne sok értelme, mivel az, hogy egy objektum milyen üzenetet fogad el, futásidőben dől el), elég az, hogy ha az objektum válaszol azokra az üzenetekre, amiket az adott függvény küld neki (ha nem, akkor sincs baj, csak kivétel keletkezik). Ezzel öröklődés nélkül is megoldott a polimorfizmus, ami nagyon nagy szabadságot ad.

Ezzel a fajta típusossággal kezdenek ismerkedni a statikusan típusos nyelvek, a C# 4-es változata is fog ebből a paradigmából "csipegetni".

Lehet, hogy nem ertem, de ez szerintem nagyon ciki!

Tetelezzuk fel, hogy van egy tobbtizezer sorbol allo Ruby alkalmazasod es van benne tobb osztalyod is hasonlo nevu metodusokkal. Egyszercsak ugy dontesz, hogy az egyik osztalynak atnevezed az egyik ilyen metodusat. Hogyan fogod megtalalni a kodban az osszes helyet, ahol a regi metodus hivodott meg..?

Szerk: Pontosan annyira, mint amikor Java-ban a semmiből előugrik egy RuntimeException :)

Javaban azert altalaban tudod, hogy ennek hol van eselye, es ha van ertelme, lekezelheted. Ruby-ban viszont nem lenne praktikus mindenhol lekezelni ezt metodushivasnal...

----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"

Erre találták ki a refactoringot.

Igen, kivancsi vagyok, hogy egy dinamikus nyelvnel ezt mennyire lehet automatizalni...

Feltetelezzuk, hogy tobbtiz koder dolgozik azon a bazinagy progin. Megvaltoztatsz egy interfeszt, amelyre tobb kollegad is alapozhat sajat moduljukban. Feltetelezve, hogy letezik mukodo refactoring eszkoz Ruby-hoz, mondjuk, hogy a te valtozatodban sikeresen atirod mind az 634 elofordulast. Innen mar a te felelosseged, hogy felhivd az osszes kollegat figyelmet a modositasra! Ha elfelejtened, vagy egy kollega elfelejtene modositani a sajat moduljat, maris +1 bug!

----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"

"Igen, kivancsi vagyok, hogy egy dinamikus nyelvnel ezt mennyire lehet automatizalni..."

Pont erről beszéltem, hogy szívás, méghozzá nem kicsit.

a) Backward compatibility
b) Változtatások szükségességének és következményének megvitatása a többiekkel, majd azután módosítás.
c) Ha valaki módosít valamit, nézze már meg a munkája következményét. És itt jön be az, hogy egy kicsit kevésbé dinamikus nyelvben is már rengeteg minden kibukik fordítási időben.

----------------
Lvl86 Troll

Ha valaki módosít valamit, nézze már meg a munkája következményét.

Igen, csak egy bazinagy rendszernel osztott fejlesztes eseten ez legfeljebb nagyon nehezen kivitelezheto.

A dinamikussag miatt elveszitett vedohalo nagyobb rendszer eseten rengeteg szivast fog okozni, amiert szvsz a dinamikus nyelvek nem alkalmasak erre!

----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"

"A dinamikussag miatt elveszitett vedohalo nagyobb rendszer eseten rengeteg szivast fog okozni, amiert szvsz a dinamikus nyelvek nem alkalmasak erre!"

Nem értem a problémád, teljesen egyetértek veled. Nekem is ezért van már tele a hócipőm a PHP-vel és a JavaScripttel.

----------------
Lvl86 Troll

Oke, akkor most egy kis bemutato:

http://orderly-json.org/

http://ajaxian.com/archives/jsonschema-chrome

http://php.net/manual/en/language.oop5.typehinting.php (ez raadasul meg ki is nez valahogy)

Ha nagyon kell, az a vedohalo bekapcsolhato. Egy AWK-script kerdese, hogy a PHP vagy a javascript a build reszekent tipusellenorzest csinaljon, megnezze, mindenhol ott-e a hinting, illetve mindketto raveheto arra, hogy hibat dobjon hibas parameterezesnel futasidoben.

Mindossze annyi, hogy az esetek tobbsegeben erre nincs szukseg, igy opcionalis.

Ha meg ennyire utalod a javascriptet, egy ora, komolyan, de nezd meg: http://video.yahoo.com/watch/111593/1710507

Altalaban ezutan meg szokott valtozni az emberek velemenye a JS-rol. A javascript valojaban egy C-szintaxisu LISP (vannak oda-vissza regexp alapu LISP-forditok), csak van egy kenyelmetlen es bugos DOM API, amirol maga a nyelv nem tehet.

Azert szoktunk refaktorolni PHP-ben is (refactor, nem rewrite), teny, hogy nem minden refactor-mintat lehet rajt alkalmazni, joreszuket a vim + regexp is megeszi, ha a PHP kodstilus korlatozott, rosszabb esetben azert a PHP-nak meg mindig van metamodellje sajat magarol (PHP CodeSniffer, php.net/reflection ), tehat aze' megoldhato... teny, hogy zend studioba jo reszukre nincs gomb:

http://files.zend.com/webinar/Code_Refactoring_Studio7/Code_Refactoring…

(Igen, az ott eclipse, azon alapulnak a nepszerubb PHP IDE-k is)

Altalanossagban dinamikus nyelveknel kevesbe kell refactor, ha jo programozoid vannak, nem azert, mert tokeletesek, hanem mert pont hogy a strict typing hianya lehetove teszi hogy anelkul cserelj ki valamit, hogy a tobbi eszrevenne. Ha meg anelkul valtoztatsz meg valamit rendszerlibbe, hogy a tobbieknek szolnal, nos, ez azert javaban is fog problemakat okozni, nem kicsit.

Ha meg anelkul valtoztatsz meg valamit rendszerlibbe, hogy a tobbieknek szolnal, nos, ez azert javaban is fog problemakat okozni, nem kicsit.

Megsem akkorat, mint egy dinamikus nyelvben!

----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"

Errgh.. DE?

Te most kerultel ki egyetemrol, vagy volt mar a csapatodnak ownershipje 100 000 sor feletti programokhoz?

Nem nagyon mernek nyulni az emberek, automatikus refactor ide vagy oda, a java rendszereknel sem a kozponti komponensekhez, hidd el.

Hogy csak egy peldat mondjak: Dependency Injection eleg erzekeny tud lenni a refactorra, es sztringek vannak meg XML fajlok, ami aze' veszelyes tud lenni.

(BTW, komolyabbnak tunnel, ha felkialtojel nelkul is irnal mondatokat.)

vagy volt mar a csapatodnak ownershipje 100 000 sor feletti programokhoz?

Jelenleg egy ~900.000 soros progin dolgozunk. Nyilvan megbeszeljuk a nagyobb modositasokat, de ezt altalaban eleg magasabb szinten tennunk.

A compiler/IDE megtalalja a modositando reszeket, ahogy lennebb mar irtam, nemigen fordulhat elo, hogy valahol a regi parameterekkel hivodik meg a fuggveny, vagy a regi, mar nem letezo nevvel...

----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"

Na, ez itt sem.

Kovetkezo okokbol:
- A fuggvenyatnevezeseket eppugy lekezeli az eclipse, kiveve, ha reflection-t hasznalnak (de ez javaban eppugy problema)
- Mivel a parametereknek nincs tipusuk, igy ha gondoskodunk arrol, hogy a felvett bemenet tipusa is megvaltozzon, vagy vegzunk egy tipusellenorzest az atirt fuggvenyben, es aszerint jarunk el, vagy - nekem ez a legtobb eset - a parametervaltozas egy uj kidefaultolhato parameter hozzaadasat jelenti, akkor itt se lesz problema.

Ertem en hogy vedohalo, meg nyilvan nagyobb code coverage-et er el a szigoruan tipusos nyelvben irt rendszer (ha nagyon akarom, PHP-ban is force-olhatom a hintinget, es hirtelen szigoruan tipusos lesz), de azert nem kell megijedni, jo programozokkal, meg megbizhato code review processzel nem lesz semmi baj.

Gyenegen tipusos, hatekony nyelvnel, lehet hogy kozel sem lenne 900000 soros a projekt, nem epulne hasonloan komplex libraryikra, igy kevesebb is lenne a problema.
Python/Perl es feldattol fuggoen akkar funkcionalis nyelvekkel gyakran sokkal produktivebb lehet a csapat, mint pl. JAVA-val. Gyakran joval kevesebb ember is eleg a munka adott ido alatti elvegzesehez.

Amit nem lehet megirni assemblyben, azt nem lehet megirni.

Erre vannak refactoring eszközök. Nyílván nem olyan egyszerű a refactoring, mint egy statikusan típusos nyelvben, és bizonyos esetekben nem is lehet, olyan helyeken, ahol dinamikus eljáráshívás történik (ilyen van Java-ban is).

Másik dolog az ilyen esetben a csapatkommunikáció. Én egy kisebb cégnél dolgozok, de ha valami olyanba nyúlok, ami mások munkáját érintheti, akkor valahogyan szólok nekik, hogy mit csinálok, és dokumentálom azt.

Harmadik dolog, hogy illik előre tervezni, ahol lépten-nyomon metódusátnevezések vannak, ott más, fontosabb bajok is vannak. Ha mindenképpen muszáj, akkor fel kell rá készülni (Java-ban sem elég csak rábökni a refactor gombra).

Negyedik a tesztek.

Ha jól értem, akkor pl. Ruyben teljesen megszokott dolog az, hogy kivétel keletkezik? Javanál ez nem így van, ott a legtöbb könyvben azt írják, hogy a kivétel tényleg csak kivételes esetben keletkezzen, illetve try-catch-ben lévő kódot kevésbé tud a JVM optimalizálni stb...

A fent említett és gthomas példáját Javaban úgy tudom elképzelni, hogy egy függvény/metódus egy paramétert vár el, ami valamilyen interfész. Ha implementálják az interfészt, akkor az interfészben definiált metódusokon (pl. read()) ki tudom szedni belőle az adatokat/üzeneteket és még kivétel sem keletkezhet, mert ha nincs implementálva le sem fordul.

Egyébként kösz a hsz.e-eket, hasznos információk és tényleg más szemlélet. Remélem még páran írnak ide, esetleg példa kódok is jöhetnének, amivel egyes dolgokat jobban be lehet mutatni.

Írok egy példát a rugalmasságra. Tegyük fel van valami egyszerű képletünk, amit használni szeretnénk:

def keplet(a, b, c)
a*b/c
end

Ezt írd át úgy Java-ra, hogy működjön a primitív típusokra, akármilyen custom osztályra, aminek van szorzás és osztás metódusa, és ne felejtsd el a BigInteger és BigDecimal osztályokat sem :)

Az említett példádra (read()) valami ilyesmit tudok elképzelni Ruby-ban:

begin
something.get_output_object.write message
rescue
# itt lekezeljük a hibát, ugyanis legtöbbször mindegy, hogy azért nem tudtuk kiírni az üzenetet, mert nincs olyan metódus, vagy mert nem lehet oda írni valami más okból (lemez betelt, kevés jogosultság, hálózati kapcsolat megszakadt stb)
end

Tegnap említettem én is, hogy ilyen műveletes dolgok jók, hogy minden típusra megoldja és nem kell megírni mindre. Sőt ahogy írod itt bármilyen osztály esetén menni fog, Javaban nincs operator overloading, tehát esélytelen megírni. :) (Bár pont az említett osztályokra állítólag meg fogják írni, hogy ott lehessen használni operátorokat műveletekre, talán már a 7-es Java tudni is fogja.)

Ha csak így simán írod, hogy rescue, akkor az minden kivételt elkap? Vagy ha nem, akkor valamilyen ős kivétel osztályt adsz meg, hogy kivétel elkapódjon? Külön nem válaszoltál rá, de gondolom akkor a válasz az igen arra a kérdésre, hogy itt a kivételek azok teljesen megszokott dolgok.

Atmasolom masik threadbol ide is:

Fajl letoltese HTTP-rol:
- PHP-ban:

echo file_get_contents('http://php.net/'); // tudom, egyesek szerint insecure, valojaban config kerdese

- Pythonban


import urllib2
response = urllib2.urlopen('http://python.org/')
print response.read()

- Rubyban:

require 'net/http'
Net::HTTP.start( 'www.ruby-lang.org', 80 ) do |http|
print( http.get( '/en/LICENSE.txt' ).body )
end

Java: (nem masolom be, mert hosszu): http://www.devdaily.com/java/edu/pj/pj010011/

Ideznem azert az aljat:

Listing 1 (above): The JavaGetUrl.java program shows how easy it is to open an input stream to a URL, and then read the contents of the URL.

Nyilvan ez egy kiragadott pelda, es nyilvan a tipusossag rejlik a dolog mogott, de szoval ez:

new DataInputStream(new BufferedInputStream(is));

Bocs, de ez nekem boilerplate. Nekem nem streamek kellenek, en egy sztringet kerek. Ha ez nincs meg 20 karakterbol, az gaz.

Pontosan tudom, hogy mitol ilyen hosszu, hogy mit jelent az InputStream, de ezek olyan reszletek, amiket a felhasznalo - azaz a programozo - elol a legtobb nyelv elrejt. Nyilvan nem veletlen, hogy az ecmascript-alapu flash szinte nulla ido alatt soporte ki az appleteket a piacrol. Nem tud tobbet, csak elrejti.

Ezek a dolgok a szigoru tipusossagbol kovetkeznek. A fenti pelda eleg common-case, sokkal inkabb, mint a hello world.

Javaban/.Netben is 2-3 sorral be tudunk olvasni egy sort vagy amit szeretnénk, ehhez jön 4-5 sor try-catch azaz hibakezelés. A fenti példákban nincs semmi ilyen, de gondolom azért ott is illik foglalkoznak a hibákkal. Akkor már nagyságrendileg ugyanott vagyunk, max a sorok rövidebbek, viszont itt a különböző stream-ek használatával tudsz finomítani a beolvasáson, hogy mégis hogyan történjen.

Mindegyikben el tudod kapni, de nem muszaj.

Miutan ez egy 3 soros program, aminek lenyege az URL beolvasas, ha nem sikerul beolvasnia, akkor oly mindegy, hogy elkapod-e... Hazi hasznalatu programnal az exception szovege jo hibauzenetnek is.

Nem tudsz javaban 2-3 sorral beolvasni, hisz eleve definialnod kell hozza a valtozoidat, meg egyesevel importalni a szukseges osztalyaidat, a hibakezelest se tudod kihagyni (nyilvan, mivel hulyebiztos nyelv). Itt ezeket nem kellett.

Tudsz finomitani is a beolvasason, de az esetek 90%-aban csak hasznalni akarod, nem finomkodni.

En csak azt mondom - de ez az uzenet nagyon nem akar atjonni - hogy amikor ranezel egy java kodra, akkor egy csomo olyan dolgot latsz, ami a java TECHNIKAJABOL kovetkezik, nem pedig az implementalando algoritmusbol. Ezeket - ha kodkiegeszitessel is - be kellett gepelned, de ami a rosszabb, emiatt nehez megerteni ranezesre, mit csinal a program.

Aki evek ota benne van a java technikajaba, az lehet, ugy gondolja, hogy 'de hisz itt nincs semmi plusz, hisz ez is kell, meg az is kell', bele se gondol, hogy mennyi minden plusz van - javas szlengben: boilerplate - aminek semmi koze az implementalando problemahoz, pusztan nyelvtechnologiai okokbol szukseges.

Ez az ara a helyes kodkiegeszitesnek, es a biztonsagosabb (de nem biztonsagos) refactoringnak.

"Miutan ez egy 3 soros program, aminek lenyege az URL beolvasas, ha nem sikerul beolvasnia, akkor oly mindegy, hogy elkapod-e... Hazi hasznalatu programnal az exception szovege jo hibauzenetnek is."

A probléma az, hogy nem 3 soros házi használatú programokat szokott kérni a megrendelő.

----------------
Lvl86 Troll

Ezzel a streames dologgal egyetertek. Nekem is sokszor szurta mar ez a szemem, bar azert hozza lehet szokni. Valoban sokkal kenyelmesebb sorokat meg String-eket kezelni, mint egy streamen byteokat. Ez egy kicsit hosszabb es korulmenyesebb javaban, de mondjuk hamar hozza lehet szokni, hogy milyen koroket kell leirni ahhoz, hogy megfeleloen tudd kezelni az I/O-t. Mondjuk gondolom, hogy vannak olyan apaches jarok, amelyek tartalmaznak ezek kezelesere egyszerusito fv-ket.

Nekem erről eszembe jutott mégegy dolog, méghozzá a design pattern-ek. Kezdve a legtriviálisabbal, a singletonnal:
class MySingletonClass
include 'singleton'
end

A metaprogramozás miatt sokat lehet nyerni a delegator jellegű kódoknál is.

Bár nem design pattern, de a getter/setter eljárások is elég szépen megoldhatók Ruby-val:
class MyBean
attr_accessor :prop0, prop1
attr_reader :readonly_prop
attr_writer :writeonly_prop
end

És végére álljon itt a kedvencem, a decorator pattern egyik szerintem legszebb megvalósítása:
http://pastie.org/12092
Kiemelem a használatát:
Coffee.with :milk, :whip, :sprinkles
Ez azért is jobb, mert sokkal könnyebb mondjuk valami random input után paramétereket adni neki, pl így:
Coffee.send('with', params)
Ez Java-ban sokkal hosszabb lenne.

Maximálisan igazad van, hogy Java-ban sokkal több megírni egy ilyet (nem részletezem itt a már megtárgyaltakat), ellenben véleményem szerint egy nyelv-hez hozzátartozik nem csak maga a programnyelv, hanem a fejlsztéshez tartozó/szükséges eszközök is. Lássuk be ebben messze lekörözi a Java a többi említett nyelvet, tehát summa fejlesztési időben lehet jobban jössz ki.

Java-ban is meg lehet írni, csak operátorok helyett sima eljárásokat használsz.

A rescue magában mindent elkap, de természetesen lehet megszorításokat is tenni, mivel a Ruby a C++-hoz hasonlóan bármit tud dobni.

Igen, megszokott dolgok a kivételek, lévén OOP nyelvről van szó.

Egy jó ideje írogatok már gyengén típusos nyelven (konkrétan leginkább PHP) ezt-azt. Tapasztalatom az, hogy komolyabb fejlesztésre már inkább hátráltat, mint segít.

Pl. PHP-ben előszeretettel használt megoldás az array(), mint általános "adatosztály", mert gyorsan össze lehet vele gányolni valamit, ahelyett, hogy csinálnának rá egy osztályt, mint ahogy pl. Java-ban vagy C#-ban szokás. Valamint ahhoz, hogy pl. a C#/Java IDE-kben használatos Intellisense (vagy akármilyen néven futó, de ugyanilyen funkcionalitást nyújtó) vagy refactoring megoldások jó része marha nagy workarounddal (ld. PHPDoc @var, @return és @param, stb.) vagy nehezen/nem megbízhatóan végezhető el, holott ezek többet segítenek a kódolásnál.

Nameg, arról nem is beszélve, hogy mennyi bosszúságot tud okozni egy véletlen odakeveredett null érték, egy int mezőnél... Nem egyszer futottam már bele olyan hibába, ami egy típusos nyelvben egyszerűen nem fordulhatott volna elő. Nyilvánvalóan programozói hibák voltak, csak a különbség annyi, hogy egy típusos nyelvben már fordításkor kibuktak volna, míg PHP-ban csak több órányi parajelenség kutatás után.

----------------
Lvl86 Troll

Ne haragudj, de miert is nem tudsz null-t irni egy java kodba?

public class x {
public static void hello(Integer i){
System.out.println("Hello "+i);
if (i==null) System.out.println("it is null");
}
public static void main(String Args[]){
x.hello(null);
}
}

Ja, hogy ott van primitiv tipusu int is? Na, az a kivetel.

(C-ben:

#include <stdio.h>
#include <stdlib.h>
void hello(int x){
printf("hello %d", x);
}
int main(){
hello(NULL);
return 0;
}

Ez is max warning)

"Ja, hogy ott van primitiv tipusu int is? Na, az a kivetel."

Pont ez a lényeg. ha int -t (és nem Integer -t) várok, akkor ne null-t, String-t, vagy anyámkínyját kapjak, hanem int-t. És a fenti hozzászólásomban is int-t írtam, nem Integer-t.

De pl. próbálj meg egy ilyet lefordítani nekem Java-ban plz.:

class A {}
class B {}

public static void Main(String Args[]) {
  B b = new A();
}

Szólj, ha sikerült.

C -s NULL meg azért sántít, mert nem azonos a Java féle null-l, hanem egy makró a 0-ra, ami megfelel egy int-nek.

----------------
Lvl86 Troll

Konkrét eset, amivel nagyot szívtam egyszer, az az volt, amikor egy összetettebb modul számára volt egy adattag, ami a modul számára tárolt egy globális értéket, ami csak szám lehetett.

Ha jól emlékszem egy gépelési hiba miatt nem került be az adat az adott változóba (már úgy eleve ez nehezen fordulhatna elő, ha pl. elore kellene definiálni a változókat) az érték.

Persze, ezek ilyen egyszerű, többnyire gépelési hibák, csak ugye míg az egyiknél nehezen fordul elő...

----------------
Lvl86 Troll

Pont a Ruby, Python rossz példa a gyengén típusos nyelvre: ezek nem gyengén típusosak, hanem dinamikusan.

Gyengén típusos a Perl, PHP.

Python, Ruby dinamikusan típusos: a változó egy objektumra hivatkozik, az bármilyen típusú lehet, de van egy konkrét típusa.

Amúgy a Pythonban létezik egy "duck typing" programozási logika: ha valami úgy jár, és úgy hápog, mint egy kacsa, akkor az kacsa.
Azaz nem a típus számít, hanem hogy a megfelelő metódusokat lehessen rajta hívni. (Pl. File helyett jó bármi, ami tud mondjuk read()-et, ha csak az kell).

+1
statikus típus, dinamikus típus
gyenge típus, erős típus

Úgy is lehet elképzelni mint egy koordináta rendszer két tengelyét.

Nekem többet mond a nyelvről, ha a két fogalmat megkülönböztetve használják, de:

- azért persze a gyakorlatban talán nem teljesen ortogonálisak a tengelyek, azaz van olyan tulajdonság aminél talán nem egyértelmű melyik tengely esetén döntő

- Jelenleg ez kb. olyan terminológiai vita mint a hacker/cracker pár. A népesség kis hányada 200%-osan meg van győződve róla, hogy a két fogalmat meg kéne különböztetni, ennek ellenére a többség szinonimaként használja...

Hahó!

Mivel itt Java-s hivatkozásokat láttam a hozzászólásokban, igyekszem ezt felhasználva hozzászólni.

,,[...] Ekkor viszont az a probléma áll fent, hogy visszakapunk egy objektumot, amiről nem tudjuk, hogy milyen metódusait hívhatjuk meg, mert elvileg nem tudjuk a típusát. [...]''

Bocsánat, de Java-ban ott az instanceof, C#-ban pedig az is operátor, amellyel le lehet kérdezni, hogy egy adott objektumpéldány egy adott osztályból került -e példányosításra, tehát meg lehet állapítani (véges lépésben), hogy egy adott objektumreferencia milyen osztály példánya, s ezáltal milyen metódusai is vannak.

Megjegyzés: A futtatórendszer -- a JVM ill. a .NET -- természetesen ezeknél a rendszereknél tudja, hogy az az Object statikus típusú referencia milyen dinamikus típussal rendelkezik, tehát milyen metódusai is vannak.

G.
===========================================
"Share what you know. Learn what you don't."

"Bocsánat, de Java-ban ott az instanceof, C#-ban pedig az is operátor, amellyel le lehet kérdezni"

Melyik a szebb és melyik hibás használata fog már kiderülni futásidőben?

1. megoldás:

public void Foo(object baz)
{
  if (!(baz is Bar))
  {
    throw new Exception("Hülye vagy, a baz nem Bar leszármazottja!");
  }

  // csinaljavalamit
}

2. megoldás:

public void Foo(Bar baz)
{
  // csinaljavalamit
}

--

PHP-vel is az a bajom, többek között, hogy ugyan van type hinting, de az csak osztályokra és resource-kra, primitív típusokra már nem alkalmazható. Pl. ha boolt várok, akkor mindenképp le kell ellenőriznem, hogy az valóban bool-e.

Juj, ha már PHP: a mindenféle cseles automatikus casting is tud meglepetéseket okozni az embernek.

----------------
Lvl86 Troll

Egy grep kerdese, meg magyar jelolese...

function valami($b_param){
// boolean-t varok
}

esetleg:
function valami($b_param){
assert(is_boolean($b_param));

//.... kod tobbi resze...
}
Sot, csinalhatsz keretrendszert:

function check_params($paramstr, $argArr) {
$paramstr = explode($paramstr, ",");
foreach($paramstr.... // nem irom tovabb)
}

function valami($b_valami, $s_masik){
check_params("bool;string", func_get_args());
}

(Egyebkent en pont a booleanre baromira nem ellenoriznek. Te tudod.)

De ez OPCIONALIS. Az esetek tobbsegere nincs ra szukseged.

Szerk:

ja, es ha nem tetszik - mindig be lehet rakni egy compile lepest, es akkor lehetnek booleanjeid is, ugy, ahogy szeretned:
function valami(boolean $valami, ClassType $masik) { }

ezt elmented .tphp -kent (typed php, hasra), es awk-val vegigsetalsz rajt.

Ha már említetted, ismer valaki a java iContract-jához hasonló DBC eszközt php-hez?
Ilyenre gondolnék:

/**
* @pre $oszto != 0
* @post is_int($hanyados) === true
*/
function osztas($osztando, $oszto) {
$hanyados = $osztando/$oszto;
return $hanyados;
}

(csak példa)
Assertekkel megcsinálható, de valami szebbnek/dinamikusan be-kikapcsolhatónak jobban örülnék.

Kedves saxus!

Csak megjegyzésként:

az Általad adott
,,
public void Foo(object baz)
{
if (!(baz is Bar))
{
throw new Exception("Hülye vagy, a baz nem Bar leszármazottja!");
}

// csinaljavalamit
}
''

kódról valóban nem derül(het) ki forrásidőben, hogy jól működik -e, mert (szvsz) le sem fordul.

Ennek okai:
a.) a throw utasítás eredményét a metódus nem kezeli le (hiányzó try-catch),
b.) vagy, ha az nincs, akkor a metódus fejléce nem tartalmazza azt a kivételt (+ hiányzik a throws kulcsszó is), ezzel jelezve, hogy azt nem helyben kell esetleg kezelni...

De valóban igazad van, a Java eléggé bonyolult nyelv, s van még mit javítani rajta...

G.
============================================
"Share what you know. Learn what you don't."

Ez C# kód, nem Java. (is -t használtam, nem instanceof-t, object-t és az elnevezési konvenció se Java-s)

Ettől függetlenül szerintem elbeszélünk egymás mellett: én arról beszélek, hogy szerintem jobb, ha futásidőben kiderül az, hogy valaminek olyat próbálunk átadni, amit nem lehet, mintha futásidőben kellene exceptionokat elkapdosni. Utóbbi eleve plusz egy olyan dolog, amivel foglalkozni kell:

1) ki kell találni, hogy hogyan kezeljük le az exceptiont.
2) Olyan hibalehetőséget hagyunk a programban, hogy adott metódusnak átadhatunk olyan objektumot, ami egyébként nem jöhet elő egy statikusabb típuskezelésnél.

Látszólag utóbbi nem okozhat sok problémát normális tervezés mellett, de sajnos az a tapasztalatom, hogy méretesebb projektnél (legyen mondjuk 100Kloc nagyságrendtől) már okoznak, mert a programozók is emberek.

Holott egyébként nem biztos, hogy foglalkozni kellene ott ilyennel (triviális a kód, nem dob exceptiont, ha mégis, azt nem biztos, hogy ott kell elkapni, stb.)

Tapasztalataim szerint ezekre jó megoldás az átgondolt interface-k tervezése, a method overloading és a generikusok. Bár azt időnként végig kell gondolni jól, mert be lehet szívni ilyen finomságokkal, amikor az ember először azt hiszi, hogy jól jár, utána viszont bonyolítja az életét:

(C#)

class Akarmi {}
class Valami: Akarmi {}


class Cucc<T> {}
class CsudaOsztaly: Cucc<Valami> {}


Bar bar = new CsudaOsztaly();
Foo<B> fooOke = (Foo<Valami>)bar;  // ez még megy
Foo<A> fooFail = (Foo<Akarmi>)bar; // Cannot convert type 

Tény, hogy kissé elborult a példa, de egyszer már sikerült olyat látnom, hogy ilyen kelljen. Reflection lett a vége.

(Hozzáteszem, ha jól fogtam fel, C# 4.0-tól már bizonyos esetekben ezt is lehet. CLR elvileg eddig is képes volt generikusokat castolgatni ilyen módon, csak a C# nem támogatta. Nincs nálam most eszköz, hogy kipróbálhassam, szóval ez erősen FIXME).

----------------
Lvl86 Troll

Figyu, ha nagyon kell, lehet am forditasi idot rakni dinamikus nyelvekbe is, tipikusan be kell tartanod nehany konvenciot aztan szevasz.

A PHP ebbol talan meg engedekenyebb is mint a ruby, irhatsz CodeSniffer plugineket, amik elintezik az ilyen ellenorzeseket, es hibat dobnak,ha nincs meg valami.

A programozok is emberek, pont ezert szokott lenni java kod tele ures catch-phrase-ekkel, amik miatt mondom azt, hogy hibas gondolat, hogy mindenkepp le kell kezelni az exceptiont. Nyilvan neveles es code review kerdese persze.

Azt mondják, hogy üres catch ágat nem lenne szabad hagyni, de azért futottam már bele olyanba, hogy egyszerűen nem lehetett elkerülni, de ez az esetek elég csekély százaléka. Magyarán ha túl sok helyen nem kezeled a hibát, az valószínűleg hibás működést eredményez a kód méretével arányosan.

A helyzet, hogy inkább a hiba ,,kezelése'' okoz hibás működést. Mit jelent a hiba kezelése? Az üres catch ág nyilvánvalóan nem hibakezelés, hanem hiba elnyomás. Rosszabb, mint a semmi. Mit csinálsz, ha nem lehet megnyitni egy fájlt? Hiába kapod el a kivételt, akkor sem lehet megnyitni. Nyilvánvalóan nincs mit csinálni, hanem feljebb kell engedni a kivételt, hátha feljebb valaki majd értelmes dolgot tud vele kezdeni.

A Jáva tankönyvek tele vannak rossz példákkal a káros kivételkezelésre. A szabály ui. nem az, hogy a kivételeket kezelni kell. A szabály helyesen: a kivételeket _jelenteni_ vagy kezelni kell. A gyakorlatban többségben vannak azok az esetek, amikor a jelentés a célszerű (throws Exception).
--
CCC3

Egyet értek a mondandóddal, én csak arra akartam célozni, hogy igen csekély esetben van az, hogy el lehet "nyomni" a hibát (ez sem minden esetben áll fent, de lehet attól, hogy nem sikerült a log-ba írni még nem akarom, hogy a művelet hibaüzenetet adjon a felhasználónak), viszont a nyelv nem engedi, hogy kezeletlen legyen a hiba, de továbbdobni nem akarom.

Én csak pythont használtam igazából gyengén típusos nyelvek közül.

Szerintem, ha mondjuk van egy listád, amibe belerakhatsz akármi tipusú objektumot, akkor nem az a lényeg, hogy tudd, az adott típus micsoda, hanem az, hogy minden típus implementálja ugyanazokat a függvényeket (vagyis legyen egy egységes interfész).

És mindegy, hogy szám.print(), vagy utf8szöveg.print(), base64szöveg.print(), asciiszöveg.print() vagy fájl.print(), vagy hang.print() a hívás, mind érvényes, és mind csinál valamit. Mondjuk pl. a hang visszatér semmit nem csinálva, esetleg kiír valami a "hang"-ra jellemző dolgot (pl. előadó, szám cím), stb.

A többi meg megjeleníti az adott objektum tartalmát.

Persze ehhez az kell, hogy az ember ezt értelmesen kezelje, megtervezze a rendszert, nem arról van szó, hogy pl. fogok egy list-et, beleteszek egy számot, egy szöveget, egy akármi objektumot, és elvárom, hogy az alaprendszer adja nekem az interfészeket, amit kitalálok.

És persze nem kötelező ezt a lehetőséget használni, van, amikor tudom, hogy ide csak számok kellenek, és hiába lehetne, nem fogok oda mást tenni - így a kezelése is mehet úgy, hogy tudom, szám jön. Csak épp a nyelv maga nem biztosítja ezt, nem ellenőrzi (csak akkor, amikor mondjuk olyan műveletet végeznék majd, ami nem értelmezett, akkor majd elhasal).

G

Szerintem, ha mondjuk van egy listád, amibe belerakhatsz akármi tipusú objektumot, akkor nem az a lényeg, hogy tudd, az adott típus micsoda, hanem az, hogy minden típus implementálja ugyanazokat a függvényeket (vagyis legyen egy egységes interfész).

Csak ezt például a Java is tudja, tehát ez sem egy olyan lehetőség amit ezek a nyelvek adnának. Eddig két dolgot írtak ami tetszik, az egyik, hogy például ha van egy kerület vagy terület számító metódus, akkor nem kell megírni int, long, float stb, hanem amilyen típust kaptam, olyanba adom vissza. A másik meg az a nézet, amit fentebb írtak, hogy egy objektumnak meghívjuk a metódusát, ha van akkor jó, ha nincs akkor kivétel. Erre reagáltam is, de még nem kaptam rá választ.

A Java ezt hogy tudja?

Én nem tudok Java-ul, csak fejlesztek benne (mert muszáj), de 2 lehetőséget ismerek:
a) Object-et várok, és cast-olom valamire, aminek van megfelelő fve (vagz sikerül, vagy nem)


olvass1(Object streamobj) {
  System.out.println(((HasReadInterface)streamobj).read());
}

b) Interface-t definiálok arra az egy fvre, hogy használni tudjam (majdnem a))

Mindkét esetbe megkötöm, hogy milyen objektumot várok. Ez a lib flexibilitását erősen rontja, így lesz sok wrapper meg fordító objektum az egyes libraryk párhuzamos használata esetén...

Nem mondom hogy használhatatlan, természetesen minden megoldható és megkerülhető, de semmiképp sem "Just Works(tm)".

Igen a "b" megoldásodra gondoltam, az interfészre. Ruby-s példánál is van egy interfész csak az egy logikai és nincs leírva sehova, méghozzá a read() metódus. Éppen ezért, mert nincs leírva nem annyira kötött, mint a típusos nyelveknél. Ez teljesen jogos, hogy egy meglévő lib-be nem tudok belepiszkálni, hogy beleírjam, hogy implementál valamilyen saját interfészt. Igen itt már érzem (remélem lesznek még további példák :)), hogy olyat ad egy ilyen nyelv amit erősen típusosnál ugyan meg lehet oldani, de tényleg plusz munkával és esetleg nem is túl szépen. (Pl. wrapperek, esetleg reflection-nel metódus hívás.)