Typový system versus unittesty

SB

Re:Typový system versus unittesty
« Odpověď #450 kdy: 26. 06. 2018, 14:34:33 »
Kód: [Vybrat]
data Obj {
    a :: Int
  , b :: Text
  , c :: [Int]
  , d :: NonEmpty Int -- neprazdne pole
  , e :: Maybe Double
  , f :: UTCTime
}
instance FromJSON Obj where
  parseJSON = withObject $ \o ->
     Obj <$> o .: "alpha" <*> o .: "beta" <*> o .: "cyril" <*> o .: "delta" <*> o .: "edward" <*> f .: "ftime"
...až na to, že tohle je kód, který je dost důsledně kontroluje, jestli ve zdrojových datech nechybí atributy, jestli to pole "delta" je fakt neprázdné, jestli tam někde nejsou nully.... a teď mi řekni, jak bys to psal v dynamickém jazyku a jak by vypadaly unit testy....

Tak především v OOP je (z podstaty) starostí objektu, aby si okontroloval, čím se plní, a naplnění i sám provedl (přestože to tak mnozí nedělají a omrdávají to jakýmisi dírami v implementaci, nebo anemickým modelem). Na to stačí metoda fromJson(json), fromDict(dict) atp. obsahující kontroly (klidně včetně rozsahů hodnot!) a jejich uložení. Jednotkovému testu stačí vyzkoušet, zda objekt pro daná data je možno vytvořit.

P. S.: Co je to zač hen ten paskvil, ve kterém jste to psal, nejde tomu ani rozumět...


SB

Re:Typový system versus unittesty
« Odpověď #451 kdy: 26. 06. 2018, 14:41:20 »
...v případě Typového systému máme 100% pokrytí z principu vždy...

Nikoho tu tato věta nevydráždila...?

Kit

Re:Typový system versus unittesty
« Odpověď #452 kdy: 26. 06. 2018, 14:47:26 »
dík za doplnění, asi nemá smysl to dál rozebírat, z mojeho pohledu je to slaboučké, nelíbí se mi, že musím doufat, že do toho nikdo nepošle něco s čím jsem nepočítal

To nebylo v zadání. Zcela v něm chybělo, jak se ta funkce má chovat, pokud dostane nějaké vstupy, které mají být z nějakého důvodu nevalidní.
no právě, jakékoliv podmínky bych vymyslel, furt musím doufat, že volající je dodrží

Jenže to je obráceně. Volající musí definovat, jaké vstupy má funkce předpokládat a které má odmítat. Ani jsi nedefinoval, že vstupy musí být int nebo string. Nic. Není tedy co testovat.

Mám tady i testy, které otestují chování pro rozdílné typy argumentů. Jenže jsi je nechtěl. Proč bych měl dávat do testů podmínky, které nejsou požadovány? Netestuji samotnou funkci, ale jen zda vyhovuje požadavkům na ni kladenou.

BoneFlute

  • *****
  • 1 842
    • Zobrazit profil
Re:Typový system versus unittesty
« Odpověď #453 kdy: 26. 06. 2018, 14:48:08 »
...v případě Typového systému máme 100% pokrytí z principu vždy...

Nikoho tu tato věta nevydráždila...?
Tak povídej :-)

Kit

Re:Typový system versus unittesty
« Odpověď #454 kdy: 26. 06. 2018, 14:48:47 »
...v případě Typového systému máme 100% pokrytí z principu vždy...

Nikoho tu tato věta nevydráždila...?

To jen BoneFlute trolil. Nestála za povšimnutí.


BoneFlute

  • *****
  • 1 842
    • Zobrazit profil
Re:Typový system versus unittesty
« Odpověď #455 kdy: 26. 06. 2018, 14:49:50 »
Ani jsi nedefinoval, že vstupy musí být int nebo string. Nic. Není tedy co testovat.
Počkej, počkej, máš to otestovat. Ne typovat. Hezky si to otestuj bez použití typů :-P

v

Re:Typový system versus unittesty
« Odpověď #456 kdy: 26. 06. 2018, 14:50:41 »
...v případě Typového systému máme 100% pokrytí z principu vždy...

Nikoho tu tato věta nevydráždila...?
já jsem to pochopil tak, že typová kontrola probíhá vždy nad celým kódem, ne jako test nad malou částí

v

Re:Typový system versus unittesty
« Odpověď #457 kdy: 26. 06. 2018, 14:54:54 »
dík za doplnění, asi nemá smysl to dál rozebírat, z mojeho pohledu je to slaboučké, nelíbí se mi, že musím doufat, že do toho nikdo nepošle něco s čím jsem nepočítal

To nebylo v zadání. Zcela v něm chybělo, jak se ta funkce má chovat, pokud dostane nějaké vstupy, které mají být z nějakého důvodu nevalidní.
no právě, jakékoliv podmínky bych vymyslel, furt musím doufat, že volající je dodrží

Jenže to je obráceně. Volající musí definovat, jaké vstupy má funkce předpokládat a které má odmítat. Ani jsi nedefinoval, že vstupy musí být int nebo string. Nic. Není tedy co testovat.

Mám tady i testy, které otestují chování pro rozdílné typy argumentů. Jenže jsi je nechtěl. Proč bych měl dávat do testů podmínky, které nejsou požadovány? Netestuji samotnou funkci, ale jen zda vyhovuje požadavkům na ni kladenou.
však to je přesně ono, není záruka, že volající něco dodrží

Re:Typový system versus unittesty
« Odpověď #458 kdy: 26. 06. 2018, 14:58:01 »
...v případě Typového systému máme 100% pokrytí z principu vždy...

Nikoho tu tato věta nevydráždila...?
Tak povídej :-)
Pokryti ceho a cim?

Re:Typový system versus unittesty
« Odpověď #459 kdy: 26. 06. 2018, 15:02:01 »
To by mě zajímalo, proč bys zrovna z těchto důvodů tohle psal v dynamickém jazyku....
Že je to dynamický jazyk přece bylo vaše zadání. Kdybych chtěl kontrolovat, že jsou tam správné typy, budu to kontrolovat, a jednotkové testy budou dělat to, že budou mít vstupy s různě pokaženými typy a budou testovat, že funkce pro načtení dat skončí správnou chybou. Pak vůbec nezáleží, jaký je typový systém, protože třeba v souboru na disku můžu mít ta data jakákoli, a při načtení musím kontrolovat, že načítaná data jsou validní.

já fakt nesouhlasím s:
Citace
To záleží na kontextu. Pokud se bude pohybovat v kódu, kde bude většina typů non-null, a výjimečně narazí na nullable typ, ošetří to. Pokud bude z venku neustále dostávat nullable typy a použité typy a typový systém ho budou nutit neustále to přetypovávat, bude to dělat automaticky bez přemýšlení.
a řekl bych že výše uvedený příklad ukazuje, že to prostě není pravda.
Podle mne se tady nedá na jednom příkladu ukázat, že to není pravda. Vždyť tvrdím, že když donutíte člověka něco dělat opakovaně skoro pokaždé v nějaké situaci, bude to v podobných situacích dělat bez přemýšlení. To je úplně nezávislé na programování, lidé takhle automaticky odklepávají potvrzovací dotazy, pokud se jich aplikace pořád ptá na nějaké nesmysly (třeba jestli má smazat soubor), vjíždí na železniční přejezd, když nebliká červená – a úplně stejně budou do programu automaticky vkládat konstrukci na přetypování na non-null typ, pokud to tak budou používat ve většině případů. Jako příklad budiž třeba implementace escapování jako obrany před SQL injection nebo XSS – pokud se to někde nechalo tak, že si to musel každý ve svém kódu ošetřit sám, bylo vzápětí escapování všude, takže se jeden řetězec escapoval několikrát (což samozřejmě nefungovalo). Dotyčný autor nad tím vůbec nepřemýšlel, jestli je v kontextu kdy musí nebo nesmí escapovat, prostě tu konstrukci bez rozmýšlení mydlil všude.

A ze stejných důvodů nesouhlasím s tímto:
Citace
I tenhle typ chyby se bude objevovat u sebelepšího typového systému, protože když programátora nenapadne, že to je zajímavý případ a měl by na to udělat test, nenapadne ho ani ošetřit to v typovém systému.
Protože například u té nullpointerexception je prostě používání "Maybe" typu na úplně jiné úrovni než dávat někam asserty a psát k tomu unit testy.
Rozlišování nullable/non-null typů je myslím extrémní případ, kdy je použití extrémně snadné a pro programátora srozumitelné. Souhlasím s tím, že tohle je případ, kdy je řešení pomocí typů nejlepší řešení. Ale jen o málo složitější věci by typově správně byly nesmírně komplikované, a nebo tam zase musíte povolit potenciální chyby, a programátor to neuhlídá.

Zkuste vzít jako příklad jednoduché porovnání dvou čísel splňující kontrakt „pokud je první číslo větší, vrací zápornou hodnotu, pokud jsou stejná, vrací nulu, pokud je druhé číslo větší, vrací kladnou hodnotu“. Nejjednodušší řešení je čísla odečíst – jenže se může stát, že dojde k přetečení. Může se to ošetřit typově – při sčítání nebo odčítání dvou čísel bude mít návratový typ dvojnásobný rozsah. Jenže se to začne nabalovat, a za chvíli skončíte s datovým typem, který se ani nevejde do paměti. A nebo to budete ošetřovat a pokud by došlo k přetečení typu, vyhodí se výjimka. Jenže pak zase potřebujete jednotkové testy, protože typový systém by vám možná zajistil, že program neudělá skrytě nic špatně, ale raději vyhodí výjimku, ale testy pak potřebujete na to, abyste zkontroloval, že aspoň někdy to skončí jinak, než výjimkou.

Kit

Re:Typový system versus unittesty
« Odpověď #460 kdy: 26. 06. 2018, 15:03:47 »
však to je přesně ono, není záruka, že volající něco dodrží

Zase obráceně. Volaná funkce se musí podřídit tomu, co chce volající. Zde zřejme narážíme na další konflikt FP vs. OOP.

BoneFlute

  • *****
  • 1 842
    • Zobrazit profil
Re:Typový system versus unittesty
« Odpověď #461 kdy: 26. 06. 2018, 15:09:31 »
...v případě Typového systému máme 100% pokrytí z principu vždy...

Nikoho tu tato věta nevydráždila...?
Tak povídej :-)
Pokryti ceho a cim?

Měl jsem na mysli trivialitu v tom, že:
Kód: [Vybrat]
fn foo(Byte: x):
je ekvivalentní:

Kód: [Vybrat]
fn foo(x):

fn test_foo():
    foreach x in [0, 1, 2, 3, 4, 5 , 6, ... 255]:
        foo(x)

fn test_foo_fail_string():
    foreach x in ["a", "b", "c", ... "aa", "bb", ...]:
        exceptedException
        foo(x)

fn test_foo_fail_float():
    foreach x in [1.1, 1.2, 1.3, ...]:
        exceptedException
        foo(x)

a vlastně ten rozvoj je nekonečný...

v

Re:Typový system versus unittesty
« Odpověď #462 kdy: 26. 06. 2018, 15:17:12 »
však to je přesně ono, není záruka, že volající něco dodrží

Zase obráceně. Volaná funkce se musí podřídit tomu, co chce volající. Zde zřejme narážíme na další konflikt FP vs. OOP.
možná - "make illegal states unrepresentable"

andy

Re:Typový system versus unittesty
« Odpověď #463 kdy: 26. 06. 2018, 15:53:57 »
Citace: Filip Jirsák
Kdybych to psal v dynamickém jazyku, tak asi proto, abych typy nemusel řešit a poradilo si to s co největší škálou různých (i ne zcela správných) vstupů.
...
To by mě zajímalo, proč bys zrovna z těchto důvodů tohle psal v dynamickém jazyku....
Že je to dynamický jazyk přece bylo vaše zadání.
Nějak nerozumím vašemu logickému uvažování. Giving up.

andy

Re:Typový system versus unittesty
« Odpověď #464 kdy: 26. 06. 2018, 16:08:30 »
Kód: [Vybrat]
data Obj {
    a :: Int
  , b :: Text
  , c :: [Int]
  , d :: NonEmpty Int -- neprazdne pole
  , e :: Maybe Double
  , f :: UTCTime
}
instance FromJSON Obj where
  parseJSON = withObject $ \o ->
     Obj <$> o .: "alpha" <*> o .: "beta" <*> o .: "cyril" <*> o .: "delta" <*> o .: "edward" <*> f .: "ftime"
...až na to, že tohle je kód, který je dost důsledně kontroluje, jestli ve zdrojových datech nechybí atributy, jestli to pole "delta" je fakt neprázdné, jestli tam někde nejsou nully.... a teď mi řekni, jak bys to psal v dynamickém jazyku a jak by vypadaly unit testy....

Tak především v OOP je (z podstaty) starostí objektu, aby si okontroloval, čím se plní, a naplnění i sám provedl (přestože to tak mnozí nedělají a omrdávají to jakýmisi dírami v implementaci, nebo anemickým modelem). Na to stačí metoda fromJson(json), fromDict(dict) atp. obsahující kontroly (klidně včetně rozsahů hodnot!) a jejich uložení. Jednotkovému testu stačí vyzkoušet, zda objekt pro daná data je možno vytvořit.
Ale tohle je diskuze nikoliv OOP vs FP, ale typy vs. unit testy (případně bez typů). Jinak moje praktická zkušnost myslím s "gson"em je ta, že mi to poctivě dávalo "null" do chybějících atributů (při generickém parsingu) a nebylo ochotné se to nechat přesvědčit jinak. Že by to samovolně zareagovalo na @NonNull, tak to už vůbec...
Citace
P. S.: Co je to zač hen ten paskvil, ve kterém jste to psal, nejde tomu ani rozumět...
Haskell. To první je deklarace typu. Ta řádka je v podstatě "Obj (o .: "alpha") (o .: "beta") ..", .: je oprátor "najdi v dictionary", akorát to celé běží v nějakém Parser monadu (což tady v podstatě jsou Checked exceptions), proto tam jsou ty divný '<$>' a '<*>' mezi jednotlivýma parametrama. (dá se to napsat i jinak, ale když si to člověk umí "zazávorkovat", tak je to takhle docela přehledné....)