Why most unit testing is waste
Az egységtesztelésnél elkövetett gyakori hibákról, javaslatok a helyes használatra
■ H | K | Sze | Cs | P | Szo | V |
---|---|---|---|---|---|---|
25 | 26 | 27 | 28 | 29 | 30 | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 1 | 2 | 3 | 4 | 5 |
Ez egy jó írás. Ennek kapcsán
Szomorú, hogy tapasztalatom szerint a legtöbb fejlesztő nem igazán érti a tesztelés inherens korlátait, ami egyrészről rengeteg elvesztegetett munkát, másrészről hamis biztonságérzetet és végeredményben rossz kódot eredményez.
Tesztelés korlátainál a unit
Mert önmagában unit tesztelés nem tud mindent lefedni, érdemes kiegészíteni másfajta tesztekkel is.
Nem, arra, hogy ahogy
Hiába az összes teszt, amit írsz, általános jellegű garanciát ezek semmire nem jelentenek (szemben a formális analízis különböző fajtáival, mint például egy statikus típusrendszer).
Tesztelni kell, és van, amit érdemes automatizáltan tesztelni, de ez semmi mást nem jelent, mint hogy kipróbáltam a kódomat egy célzottan megválasztott bemenetre.
Ahogy arra Rich Hickey hívja fel a figyelmet:
Formális analízis témában
(a statikus típus példád értem azt hiszem, ott csak azt nem értem, hogy az hol kapcsolódik a formális analízis témához... de lehet, hogy az egészet félreértem és ennek kicsit más a funkciója, mint a unit-, funkcionális- stb. teszteknek)
Válaszolok magamnak: infokukac.com
a statikus típus példád értem
A típusellenőrzés egy formális módszer. Egy statikus nyelvben deklaratívan meg tudom adni, hogy egy művelet eredménye milyen érték lehet, és a fordító garantálja, hogy soha nem lesz más; egy dinamikus nyelvben írhatok rá egy tesztet, ami garanciát még arra sem fog adni, hogy ugyanazokra a bemeneti értékekre ugyanazt fogom eredményül kapni.
A kettőnek nem ugyanaz a funkciója, nyilvánvalóan nem lehet mindent formálisan ellenőrizni. Engem csak az ijeszt meg, amikor állítólagos szakemberek szájából elhangzik az az érv, hogy „a statikus típusrendszer felesleges, hisz úgyis kell tesztelni!”, illetve, amikor egy kódbázis minőségét az alapján ítélik meg, hogy vannak-e hozzá unit tesztek, egy refaktorálást pedig ipso facto sikeresnek könyvelnek el, mert futnak a tesztek.
"Engem csak az ijeszt meg,
+100
Abszolút igazad van. Szerintem az ilyesmi akkor szokott bekövetkezni amikor valaki megfeledkezik arról, hogy minden eszköz csak eszköz és annyit ér amennyit a felhasználója...a "unit teszt"-re valóban sokan csodaszerként tekintenek, ez hiba. De azért abba a hibába se kéne beleesnünk, hogy azt sugalljuk, hogy nem fontosak. Mindennek megvan a helye.
Engem csak az ijeszt meg,
A refaktorálás utáni sikeres tesztekre is érvényesek Dijkstra, illetve Rich Hickey állításai. Vagyis a sikeres tesztek csak azt bizonyítják, hogy nincs velük kimutatható jele az átgyúrás kudarcának.
No de pont ez a lényege a TDD-nek. Nem garantál semmit, csak detektál.
Korábban ezt írtad:
Ugye erős típusosságra gondoltál? Merthogy például a C statikusan típusos, de a compilerek hibajelzés és figyelmeztetés nélkül lefordítanak neked olyan hibás kódot is, amitől elsírod magad, ha jól megnézed... Gondolj csak az automatikus típuskonverziókra. A C statikusan, de gyengén típusos.
A lényeg az, hogy ne
definíciók - erősen és gyengén, statikusan és dinamikusan
A dinamikusnak az lehetne, amit írsz, vagyis fordítási időben nem ismert a változók típusa. Közelebbről megnézve persze kiderül, hogy a dinamikusan típusos(nak nevezett) nyelvekben is meghatározható, illetve behatárolható egyes változók típusa a program futása előtt, de nem az összes változóé. Példa: a műveletek általában impliciten jelzik, hogy numerikus típusú egy változó, string, vagy mondjuk logikai, illetve skalár, tömb, vagy összetett (record, struct stb.). A statikusan típusos nyelvekben is egyre inkább támaszkodnak erre a fordítók készítői, mert lehetővé teszi a típusmegadás elhagyását. Friss példa a Go. Nem kell leírni benne, mégis egyértelmű, hogy az x := 3.14 utasításban x lebegőpontos változó, ez egyben típusdeklaráció is, 64 bites float (float64) lesz (mert a nyelvben az a default float típus).
A lebegőpontos konstansok írásmódja a legtöbb nyelvben a gépi ábrázolás pontosságát is jelzi. A kettő együtt precízen meghatározhatóvá teszi a változó típusát? Nem feltétlenül, mert egyes nyelvekben az azonos gépi ábrázolású lebegőpontos változók típusa is eltérhet, ha úgy definiálod, hogy az egyik típus például hosszúságot, a másik tömeget, a harmadik dollárban megadott pénzmennyiséget ábrázol: 2.5 m és 3.42 kg vagy 91.8 $ nem adható össze. És persze euró sem adható össze mondjuk dollárral. Friss példa erre az F# valamelyik új verziója (eleinte nem volt benne ilyen). Adában értékintervallumot is megadhatsz a numerikus típusoknál, és ha egy művelet végeredménye kilép abból, akkor hardveres túlcsordulás nélkül is kivétel lép fel. Negatív hossznak vagy időtartamnak nincs értelme, abszolút nulla alá nem csökkenhet a hőmérséklet stb. A kivételt persze lekezelhetjük.
És a legdinamikusabban típusos nyelvekben sincs mindenről mindenre konverzió futási időben.
A dinamikus-statikus spektrum tehát nem két fokozatú, nem fekete-fehér. És még ez a legkönnyebben elemezhető kérdés a témában.
A gyengén-erősen dualitás esetében is inkább az erősség mértéke a kérdéses. A C típuskonverzióit csak példának hoztam fel, nem definíciónak szántam. Vannak veszélytelen implicit típuskonverziók, például 8 bites egészek 16, 32, 64 bitesre nyugodtan átalakíthatók, persze az előjelességüket megtartva. (Ez is csak példa.) Itt a spektrum egyik végén egyáltalán nincsenek típusok: assemblyk, Forthok, Lispek többsége (ezekből is vannak típusos fajták). A másik végén nemhogy erős típusok vannak, hanem különféle merevségű típusrendszerek, precíz elméleti háttérrel és annak könyörtelen gyakorlati megvalósításával.
Ami a kérdés lényege: mennyit segít egy nyelv (pontosabban nyelvi implementáció, értelmező vagy fordító) statikus, illetve erős típusossága a program helyességének ellenőrzésében?
Azt állítom, hogy a dinamikusan típusos nyelvek közel azonos mértékben segítenek, mint a statikusak, csak nem fordítási időben, hanem futásidőben ellenőriznek. Persze bosszantó, ha futás közben kiderül, hogy hibáztunk a program írásakor, de szerintem semmivel nem kellemetlenebb, mint amikor a compiler figyelmeztet erre, legfeljebb szokatlan és emiatt meghökkentő eleinte. A kevésbé használt programágakra ritkán kerül rá a vezérlés, emiatt egyes típushibák sokáig rejtve maradhatnak, de a gyakorlatban nagyon ritkán fordul elő ilyesmi, és akkor sem csak típushibák jöhetnek elő, mindezek ellen pedig kötelező a legalább felületes tesztelés. Másrészt futásidőben olyan információkhoz is hozzájut a futtatórendszer, amiket fordítási időben nem ismerhet, és ez további ellenőrzésekre ad lehetőséget. Most akkor a dinamikusan típusos nyelvek segítenének többet a hibák kiderítésében? Szerintem nem, a mérleg itt inkább döntetlenre áll.
Sokéves gyakorlati tapasztalatom: a Python duck typingja elegáns, tömör, jól olvasható programokat tesz lehetővé, gyorsan írható a kód, mégis erősen típusos a nyelv és ritkán okoz gondot a statikus ellenőrzés hiánya. Elég trehány vagyok, néhányszor futásidejű kivételt kaptam nem tesztelt programágakon, de ezek túlnyomó többsége nem típushiba volt! Korábban a PHP-vel sokat szívtam, mert az interpreter a legostobább elírásaimat is megpróbálta valahogy értelmezni és valamit lefuttatni. Anyáztam eleget, utáltam a nyelvet és a hülyeségemet, de nem volt rá megoldás. A PHP ad hoc ötletek kusza halmaza, Rasmus Lerdorf bevallottan nem ért a nyelvtervezéshez, nem is akarta soha megtanulni, büszke a saját hülyeségére, mint a politikusaink. C-ben az implicit konverziókkal szívtam sokat, még többet a void pointerekkel. Statikusan típusos nyelv, de a compiler alig segít, mert nagyon lazák (gyengék) a típusok. Nem a dinamikus kontra statikus kérdés az igazi.
Az erős kontra gyenge típusosság esetében viszont határozottan előnyt ad a típusok meghatározhatóságának és ellenőrizhetőségének mértéke. Minél erősebb, annál több hibát jelezhet. A statikus típusmegadás persze elkerülhetetlen, ha a numerikus ábrázolás pontosságát, értékintervallumot, az értékek dimenzióját is meg akarjuk határozni. A típusrendszer merevsége (erőssége) látszólag gúzsbaköt, valójában felszabadít, átveszi a programok megtervezésének egy részét. Ha tudatosan kihasználjuk a lehetőségeit, különösen sokat segíthet nem csak a kód írásakor, hanem a későbbi megértésében is! A compiler vagy interpreter figyelmeztetéseinek, hibajelzéseinek örüljünk, ne bosszantásnak vegyük! Helyettünk dolgozik.
Nemrég futottam bele egy típusrendszerek hasznosságát vizsgáló, alaposnak tűnő áttekintésbe. Az empirikus vizsgálatok többsége érthető okokból a kész programok minőségét vizsgálja (mennyi hiba esik 1000 kódsorra stb.), de pont emiatt nem mutatja ki az erős típusosságnak a programok tervezésekor és írásakor nyújtott segítségét. A típusvizsgálat legtöbb típushibát tervezési és írási időben segít kiszűrni, nem fordítási időben. A statikus kontra dinamikus kérdéskör is pont a két kései fázist firtatja, pedig nem a fordítási és a futási időben kapunk sok segítséget a típusok kezelésétől.
Akit a téma mélyebben is érdekel, annak a Lambda the Ultimate-et ajánlom.
Ha rám hallgattok, akkor tegyétek félre a kételyeiteket, és próbáljátok ki a Gót. Erősen és statikusan típusos. A Rust compilere segít talán legtöbbet, de nagyon lassan fordít. És ez a legtöbb erősen és statikusan típusos nyelvre igaz, mert sok munkához sok idő kell. A Go compilere és linkere villámgyors, és az is marad, mert kifejezetten így tervezték a nyelvet, egyszerűnek és gyorsan fordíthatónak. Pont azokra a problémákra adnak benne jó megoldásokat, amik annak idején oly sokat bosszantottak a C-nél. Nem váltja le a C-t, de kiváló eszköz, egyre több területen használható. Webre már ideális eszköz. PHP, Java, és a mostanában divatba jött Node.js is elbújhat mögötte. Könnyen megtanulható. Tapasztalt, vén profik tervezték és fejlesztik. Több évtizedes tapasztalataim alapján az összes döntésükkel egyetértek. Ezt egyetlen más nyelv sem közelíti meg, pedig sokat tanulmányoztam és próbálgattam. Doktrínák, kísérletezés, kétes használhatóságú elméletek, dogmák helyett tapasztalatokból kiindulva könnyen használható, sokat segítő, gyorsan fordítható és futtatható nyelvet terveztek és készítettek. Emberek, Go a jövő. Ne keresgéljetek tovább.
Menjünk
most találtam
Nekem se a típusokkal volt gondom PHP-ben (mert lényegében nincsenek, de ez csak annyit jelent, hogy az interpreter sokkal kevesebbet segít az adatok kezelésében). Mondjuk az mellbevágott, amikor idézőjelek nélküli (kapkodásban elírt), angol nyelvű (ékezet nélküli) stringeket is értelmezett valahogy az interpreter (részletekre már nem emlékszem, csak a megdöbbenésemre).
Beszéljen helyettem inkább a web :). Nem az érdekes, hogy lehet elborult kódot írni PHP-ben, hanem egyes erre használható konkrét fordulatok az izgalmasak, némelyik sűrűn előfordulhat tisztességes kódban is.
Mint C-ben ez a gyöngyszem:
Nem ennyire egyszerű, sokkal nehezebben átlátható volt a kód, itt kiemeltem a baki lényegét. Törlések, hozzáírások, hétköznapi módosítások idézték elő, teszt se mutatta ki a hibát, mert a for ciklus feltétele nagyon összetett volt, és ritkán teljesült (elvileg), mégis mindig lefutott egyszer a hozzá tartozó {} blokk.
Elnézést az offolásért.
Mondjuk az mellbevágott,
Konstansként értelmezi, aminek, ha nincs definiálva, akkor a konstans neve mint sztring az értéke. Elképesztő nyelv.
Friss példa
A lebegőpontos konstansok
Ezekre illene is önálló típust bevezetni, ha nem volna túl körülményes a legtöbb nyelvben. Az Adában nagyon szeretem ezt a lehetőséget, a legújabb C++ pedig egyedi literált is enged rendelni saját típushoz.
Állításom: a Python nem a statikus típusrendszer hiányától lesz egy fantasztikus nyelv, és annak jelenléte nem rontana lényegesen az általa nyújtott élményen.
Elhiszem, ha azt mondod, hogy úgy érzed, a te mindennapi munkád során nem térül meg számodra egy statikus típusrendszer használata, mert mondjuk Pythonban is tudsz helyes kódot írni. Vannak, akik azt mondják, hogy ők PHP-ban is tudnak. Ezek szubjektív állítások. Az, hogy egy statikus típusrendszer nem enged bevezetni bizonyos hibákat, az meg tény.
Ami azt a kérdést illeti, hogy mennyire gyakoriak a típushibák a hibák közt általában, arra a válasz leginkább embertől (egy személyiségéből fakadóan kevésbé precíz vagy fáradt fejlesztő több formai hibát fog véteni) és feladattól (egy komplex algoritmus több logikai hibát fog tartalmazni, mint egy triviális) függ. A magam részéről elég fegyelmezett vagyok, ezért hosszú évekig jól megvoltam különböző dinamikus (vagy statikus, de gyengén típusos) nyelvekkel, de egy idő után rájöttem, hogy mennyi energiát emészt fel az a fajta koncentráció, ami mondjuk ahhoz szükséges, hogy az ember C++-szal ne vágja le a kezét, és amit egy fordító a megfelelő nyelv esetén megspórol neki. Ezért váltottam például Adára. Egy kellően kifejező típusrendszer egyébként a megfelelő programozói gyakorlattal olyan hibákat is típushibákká alakíthat, és így kiküszöbölhet, amik elsőre nem tűnnek annak.
És pont az a mentalitás, ami mindig is bosszantott a C-nél. Ami nem csoda, hisz ugyanazok az emberek állnak mögötte. Ne érts félre, én nagyon szeretem a C-t, de a C a maga idejében sem számított modern nyelvnek, és nem is igazán fenntarthatóak benne nagyobb (a nagyobb egy elég kicsi definíciójára) projektek.
A Go esetében például a generikus programozás hiánya számomra érthetetlen, és a strukturális típuskompatibilitással együtt eléggé leértékeli a statikus típusrenszerét ahhoz, hogy fájó szívvel, de lemondtam róla, amikor lelkesen tanulmányozni kezdtem.
lokálisan optimális kompromisszumok
Egyetértek. Pontatlanul fogalmaztam, nem akartam hosszasan boncolgatni ezt a unit testinggel kevéssé összefüggő részletet. Pontosítom az eredeti állításomat, első lépésben csak a feltétlenül szükséges mértékben: a duck typingot statikus típusellenőrzésre cserélve, a típusrendszer további erősítése nélkül még nem nyerne semmit a Python.
(A 2. lépés már más nyelvekre is igaz:) A statikus ellenőrzések ugyanis csak szükséges, de nem elégséges előfeltételei az erős(ebb) típuskezelésnek.
(A 3. lépés az érdekes:) Tehát az a kérdés (a Python esetében), hogy mit tennénk hozzá a típuskezeléshez a statikus típusosság bevezetése után? Most vezetik be a type hintinget, függvény-annotációk formájában, persze csak a Python 3-ba. (Most fogadták el az erre vonatkozó PEP-et, a 484-es rémlik valamiért, de az tavaly szeptemberi.) No, ezzel semmit nem nyernek, rengeteg munka árán. Nyilván időt hagynak maguknak, a felmerülő ötletek és javaslatok közül ráérnek válogatni majd akkor, amikor elkészültek ezzel a munkafázissal.
Fenntartom, hogy "a dinamikusan típusos nyelvek közel azonos mértékben segítenek, mint a statikusak, csak nem fordítási időben, hanem futásidőben ellenőriznek". (A teljes bekezdést lásd a 19. hozzászólásban, nem másolom ide.) Tehát példát kérek a statikusság előnyére, a legszűkebben vett értelmében (azaz a statikusságra építő kiegészítések nélkül). Vagy inkább hagyjuk a kérdést, mert szerintem egyetértünk, és immár eljutottunk a szőrszálhasogatásig.
Így van, ha a nyelv lehetőséget ad rá, a típusrendszer sokat segít a tervezésben. Sajnos ezt sehol nem tanítják. Ja, tanítják, csak OO design néven :).
Csak azért hagyták ki, mert még nem találták meg az igazán jó implementálásának módját. (Nekem megfelelne az Adáéhoz hasonló megoldás, de ha nekik nem, belenyugszok. Még nem hiányzott.) Miután bevezetik valamilyen formában, azután nem, vagy inkább nagyon nehezen módosíthatják majd a később megírt kódok miatt. A nyelvtervezés olyan, mint a mikádó/marokkó, csak itt a tervezők építik a pálcikahalmot, nem véletlenszerűen áll elő. Roppant nehéz visszabontani valamit egy nyelvből anélkül, hogy más részeket is megmozdítanának. Sok nyelv szenved az elkapkodott, nem eléggé körültekintően hozzáadott kiegészítései miatt. Kísérleti nyelveknél nem gond, de népszerűeknél a nyelv hosszas kínlódását, stagnálását okozhatja egy-egy visszabontás. (Szerintem ne menjünk bele a részletekbe, nem annyira érdekes.)
A Gónál a generikusok hiánya
A szerkezeti típuskompatibilitás teszi lehetővé az öröklés elegáns kikerülését, az OOP előnyeinek megtartásával. Választaniuk kellett, és szerintem jól választottak.
Fuss neki még egyszer a Gónak, érdemes. Lokálisan szuboptimális kompromisszumokból álló, kiváló érték/ár arányú konfiguráció. És a tervezői priorizálták a teendőket. A nyelvtervezés nem kívánságműsor, hanem komoly mérnöki munka, sok tapasztalatot kíván és rengeteg gürcölést. (Én is foglalkoztam vele, nem jutottam messzire, de sokat tanultam belőle.)
Python type hint: ez azt
update: bocs, most tudtam csak megnézni azt a PEP-t, ez már egészen más, mint ami ezzel foglalkozó írást én láttam.
Úgyhogy a fenti storno...
A statikus ellenőrzések
Tudsz mondani példákat a lehetséges további lépésekre, mert nem tudom, hogy jól értem-e a mondandód.
Ez a magyarázat elég kínos, egyrészt azért, mert veteránokról van szó, másrészt azért, mert éles használatra késznek mondják a nyelvet.
Én szomorúan úgy gondolom, hogy a nyelv a C-hez hasonlóan a Bell csapat személyes preferenciáit tükrözi, nem pedig arra vonatkozó megfontolásokat, hogy mi segítené leginkább helyesen működő szoftverek írását a nagyvilágban.
Nyilván, hisz korábban kifejtetted, hogy megelégszel a futásidejű hibákkal is. Én csak azt állítom, hogy a generikusság hiánya aláás egy egyébként statikus típusrendszert, mert arra kényszerít, hogy megkerüld.
Az öröklődés és a típuskompatibilitás ortogonális funkciók. Ha nem akarnak öröklést, nominális kompatibilitás mellett is kihagyhatták volna. A strukturális kompatibilitás ismét csak gyengíti a statikus típusrendszert, hisz szándékolatlan kompatibilitást vezehet be típusok közt.
Én szeretnék, tényleg, mert sok nagyon vonzó tulajdonsága van. De mindig rájövök, hogy a fenti és hasonló hiányosságok túl zavaróak volnának.
Tudsz mondani példákat a
Példa: a C statikusan típusos, de gyengén. Tehát a statikus ellenőrzések önmagukban nem elégségesek az erős(ebb) típuskezeléshez (de nyilvánvalóan szükségesek).
Ha a típusrendszer lehetővé teszi a típusdeklarációk finomabb kidolgozását, a compiler gondosan ellenőrzi is ezeket a részleteket, és nem konvertál automatikusan, akkor megvalósul az erős(ebb) típuskezelés, beteljesül a lehetőség (a finom kidolgozás, a gondos ellenőrzés és az automatikus átalakítások hiánya csak példák). Példák: szinte az összes statikusan típusos nyelv. Az erős(ebb) típuskezelés egyes módjai csak statikus típuskezelés mellett lehetségesek, például a részletes típusdeklarációk. Az adatábrázolás egyezése mellett megkövetelhető például az adatokon végezhető műveletek egyezése is (egyik a másik részhalmaza legyen, nem kell a teljes egyezésük), és ezt is statikusan érdemes ellenőrizni.
Végül a "dinamikus erősebb lehet, mint a statikus" változatra példa a numerikus túlcsordulások és az index-túllépések futásidejű ellenőrzése. Ezt ugyan például sok C compiler is felkínálja fordítási opcióként, de a nyelv nem definiálja a kezelésük lehetőségeit, kivételkezelés sincs. Pythonban van kivételkezelés, és a futásidejű túlcsordulások is lekezelhetők, az indextúllépések szintén. (Egész túlcsordulás nincs, korlátlan a számábrázolás.)
Ha a Pythonnál a most bevezetésre kerülő statikus alapokra építve megvalósítható lehetőségeket firtatod, hirtelenjében ezek jutnak eszembe:
1) A változók és az argumentumok típusmegadási konzisztenciájának ellenőrzése. Azaz ha mondjuk egy pragmával előírjuk a típusmegadást, akkor mindenütt követelje meg a compiler, ahol nincs kikapcsolva a megfelelő pragmával. A standard libraryben így tetszés szerint lehetne továbbra is megadni vagy elhagyni a típusokat, kikapcsolva a típusmegadás megkövetelését. A programokban, librarykben stb. pedig a szerzők dönthetnék el, mit szeretnének: szigort és hatékonyságot, avagy rugalmasságot?
Nekem tetszene ez a lehetőség. Ennél sokkal erősebb típusosságra szavaznék ugyan, de Guido van Rossum ezt kizárta előre, ő nagyon rugalmas Pythont akar továbbra is. A fentihez hasonló kompromisszumra még látok esélyt.
2) Erősebb típusmegadási lehetőségek. Például az egészek értékkészletének korlátozása révén elérhető optimalizálások érdekében. A NumPy egy sor példát mutat erre és továbbiakra, nyilván a hatékonyabb kód érdekében. Jókora részét amúgy is C-ben írták, ha jól tudom.
Ezt kaptam rá:
NameError: name 'result' is not defined
De enélkül sem értem a példát, talán a hőhullám miatt.
Egyébként sem a tesztek mellett, netán a statikus ellenőrzések ellen érveltem (ha mégis úgy tűnik, akkor elnézést, fel sem merült bennem ez a szándék). A nagyon erős típusosság híve vagyok, az pedig csak statikus ellenőrzések révén lehetséges. Az elméleti alapokat akartam csak tisztázni, elnézést, ha nehezen érthető volt, amit írtam.
Sorra vették az ismert lehetőségeket, és egyiket sem találták kifogástalannak a továbbiakra nézve. Mindegyikkel adódnak gondok, ez tény, nem ők hozzák fel mentségnek. A dilemma lényege: vagy szörnyen kövér lesz a kód, vagy kínosan lassú lesz, vagy aránytalanul elbonyolódik egy amúgy kellemesen egyszerű nyelv.
A generikusok hiánya szerintem nem akadálya az éles használatnak. Évek óta sikeresen fejlesztenek Góban elég nagy cégek, én már elég későn kapcsoltam.
Nem kényszerít. Az egyirányú kódgenerálás elegáns megoldás egy sor problémára, erre is. A cog pedig kiváló, nyelvfüggetlen kódgenerátor. Sokat használtam már.
OCamlben nem okoz sok gondot, Haskellben sem, pedig nagyon erősen típusosak. Vannak még példák, most ezek jutottak eszembe.
Ha tartasz a váratlanul fellépő strukturális kompatibilitásoktól, megfelelő típusdeklarációval védekezhetsz az ilyen hibák ellen. Ha ki akarod használni, akkor pedig jól jön.
Nem akarlak győzködni. Az Ada és a Rust mellett valóban komolytalannak tűnhet a Go. Nem az, de annak tűnhet :).
Évek óta sikeresen
PHP-ban is rengeteg cég fejleszt sikeresen (legalábbis a siker valamilyen definíciójára). Nem állítom, hogy a Go használhatatlan, csak azt, hogy szerintem nem teszi meg a jelenlegi tudásunk szerinti legtöbbet azért, hogy előremozdítsa az iparágat.
Ha nincs jó megoldás egy megoldandó problémára, akkor választani kell egyet a rosszak közül.
Ha elegáns megoldás a problémára, akkor miért nem támogatja a Go nyelvi szinten? ;) A kódgenerálás mindig a generált nyelv hiányosságára mutat rá.
Elég rosszul kell állniuk a csillagoknak ahhoz, hogy ebből az elméleti lehetőségből gyakorlati hiba legyen, ez tény. Én azonban épp az elvi garanciák miatt propagálom a statikus típusrendszereket.
Itt mire gondolsz?
Szerintem hasonlóak az elvárásaink, csak árnyalatokban térnek el :)
A példában szereplő függvény szökőnapokon egész helyett sztringet ad vissza, ilyenkor a függvényt használó, egészet feltételező műveletek elszállnak. Egy statikus típusrendszer mellett ez a kód nem fordul le, egy dinamikus esetén a példában feltételezett specifikáció mentén írt tesztek sem fogják kimutatni, hacsak nem február 29-én futnak.
Ahogy mondjuk az Ada is nyelvi szinten támogatja ezeket. Az, hogy egy objektum futásidőben milyen állapotba kerülhet (túlcsordulás), illetve hogy adott állapotában milyen paraméterekkel végezhetők rajta műveletek (túlindexelés), az az állapotát fenntartó műveletek (nyelvi vagy felhasználói) definícióján múlik, és futásidőben dől el. Ennek nincs köze ahhoz, hogy fordítási időben ellenőrizzük-e, milyen műveleteket támogat.
Szerintem sosem fogok választ kapni arra, ez a bizonyos, a dinamikus nyelvek kapcsán sokat hangoztatott rugalmasság hol jelent előnyt :)
A Python erősen típusos, de dinamikusan. Tehát az erős típusosság önmagában nem elég a típusbiztossághoz (de nyilvánvalóan szükséges) :)
A felütésben még valóban úgy tűnt, mintha a statikus típusosság ellen érvelnél, de szerintem teljes mértékben ugyanazon az állásponton vagyunk, csak más oldalról közelítjük meg a kérdést (lásd az előző bekezdést).
Egy statikus típusrendszer
Ezt inkább nevezném tervezési hibának, mert ahelyett, hogy injektálnák a dátumot, elkérik egy globális függvénytől. Így nem tesztelhető jól a kód. TDD-vel fejlesztett kódnál nem fordulhatna elő ilyen. Szóval itt nagyjából annyit mondtál, hogy egy teszteletlen dinamikus kódnál nem jelzik a hibát a tesztek...
Ezt inkább nevezném tervezési
A példa a lehető legegyszerűbb és leglátványosabb próbál lenni, az éles hibák általában nem ilyenek.
Itt annyit mondtam, hogy ha ez a kód statikus, akkor akármilyenek is a tesztek, ha vannak egyáltalán tesztek, a kód nem fordul le.
Ez a kód egyébként lehet egy külső könyvtár része is, amit behúzol, lefuttatod a tesztjeid, és egy év múlva összedől tőle az alkalmazás.
Ez a kód ugyanúgy elszáll
from datetime import date
def n():
return randint(1, 9) if date.today().timetuple()[1:3] != (2, 29) else 10
# …
print(result - n())
Nem érzem azt, hogy bármelyik (dinamikus vs statikus) megoldás superior lenne, mindegyiknek megvannak az előnyei és a hátrányai.
Ez a kód ugyanúgy elszáll
Ez igen, a fenti nem. Én csak ennyit mondtam: bizonyos típusú hibákat egy statikus típusrendszer garantáltan kiszűr.
Én nem látom hátrányát a statikus ellenőrzésnek, ellenben látom előnyét (lásd az előző bekezdést) – ebből számomra logikusan következik, hogy az egyik hatékonyabb, mint a másik. Továbbra is meggyőzhető vagyok az ellenkezőjéről, de csak hatásos érvekkel.
Indexen produkált valaki egy
A kérdező megmérte egy PHP szkriptbe ágyazott SQL kódjának a futásidejét valahogy így:
Nem tudom, hogy beállítás kérdése-e, de ez azért nálam is kiverte a biztosítékot: hogy a fenébe csinálhat olyat egy akármilyen interpreter, hogy a számok közt is szóközt tartalmazó stringet numerikusként kezel? :(
Dokumentáció?
O.K., én megnéztem a doksit,
Figyelmetlenségből gyalogost
Szerintem ez inkább arra
Én szomorúan úgy gondolom,
Ha neked mindenképp helyesen működő szoftverre van szükséged, és ez nem valósítható meg a Go segítségével, akkor ez valóban nem a te nyelved.
No de pont ez a lényege a
Ez az, amit szerintem sokan nem értenek.
Erős statikus típusosságra.
Én most BDD-t nézegetem, azt
Szerintem a Jasmine nem BDD framework.
Nálunk sincs komoly lefedettség, de már számtalan alkalommal fogott meg olyan hibákat egy-egy teszt, ami mellett máskülönben simán elmentünk volna, az meg szerintem természetes, hogy annál ártalmasabb egy hiba, minél később fedezik fel.
Nekem is egy sima unit test
Cucumber.js
Én ezt néztem:
Néhány magyar nyelvű BDD videó
(ezek nekem segítettek helyretenni a dolgokat)
Köszi!
Szvsz, a 4 közül egyedül
CodeCoverage
+1, én eleve úgy szeretek
(Ez válasz a 30. hozzászólásra, nem tudom, miért ide csúszott)
Egyébként már lehet nyelvi szinten Go kódot generálni :).
Egyértelműen a dinamikus típuskezelésre törekvők történelmi érdeme a type hinting (típusjavaslat?), az opcionális típusmegadás, a típuskikövetkeztetés (type inference, type deduction) és hasonlók terjedése. Szigorú szemmel nézve ezek az ördög patái az ajtóban, engedékeny szemmel angyalszárnyak is lehetnek. Szerintem nyelvi megvalósítástól függően jó vagy rossz a lazább típusmegadás, mindkettőre vannak példák (jóra csak mostanában). Ha az olvashatóság rovására megy, akkor ellenzem.
Mégis kerestem erős példákat a dinamikus pártiak támogatására: Benefits Of Dynamic Typing,
Why dynamic typing?, Strongtalk.
Engem azért nem győztek meg, mert szerintem tervezési időben nyújtanak segítséget a típusok, a későbbi hasznuk jóval kisebb, és hátrányuk is van. Valamikor gondolkodni kell róluk, és ha már gondolkodtunk, akkor érdemes leírni a gondolataink eredményeit, lehetőleg a forráskódba, lehetőleg programmal feldolgozható és ellenőrizhető formában.