Zobrazit příspěvky

Tato sekce Vám umožňuje zobrazit všechny příspěvky tohoto uživatele. Prosím uvědomte si, že můžete vidět příspěvky pouze z oblastí Vám přístupných.


Příspěvky - MartinBeran

Stran: 1 [2] 3 4
16
Vývoj / Re:Co si myslíte o OOP?
« kdy: 05. 01. 2019, 23:35:38 »
Dynamické typování v tomto případě nemá žádnou výhodu. Bude to pomalejší a na chyby v programu se přijde až v runtime, kdy to celé spadne.

Při chybě v programu to spadne už v testech - stejně jako při statickém typování.

Pokud máš plné pokrytí. Za předpokladu jedné unity.

Pokud píšeš libku nebo nedejte bozi framework, tak se ti tohle ještě dál komplikuje...

K tomu mě napadá akademická otázka: Dá se pro libovolný program napsat testovací sada, která nalezne všechny chyby, jež by nalezla statická typová kontrola, a přitom nebude zahrnovat (jakkoliv zakamuflovanou) statickou typovou kontrolu?

17
Vývoj / Re:Co si myslíte o OOP?
« kdy: 05. 01. 2019, 23:25:00 »
... Ale třeba get_peer_address() má jistě smysl pro třídu network_socket. Hůř se ale bude hledat význam takové funkce pro string nebo int a s tím mi nepomůže, když tu funkci přesunu do samostatné třídy address_extractor. Jde mi o to, že některé operace nemají pro nějaké typy smysl, buď obecně nebo v kontextu konkrétního řešeného problému. Jakákoliv runtime reakce na volání takové operace s nevhodným typem argumentu je špatně, protože v prvé řadě nemělo dojít k tomu zavolání. Tedy jedná se o chybu v programu, kterou je nutno řešit úpravou kódu. Statické typování mi takovou chybu odhalí včas a bez toho, abych musel kvůli diagnostice psát víc, než jméno typu.

get_peer_address() má jako parametr int32, string nebo je to jedno? Návratová hodnota je big endian nebo little endian? Pro diagnostiku musíš psát i testy. Co když dostaneš 0.0.0.0 nebo 255.255.255.255? Bude to v pořádku?

Funkce get_peer_address() je buď metoda třídy network_socket bez parametrů, nebo je to samostatná funkce (nebo klidně metoda třídy address_extractor) s jedním parametrem typu network_socket. Návratová hodnota je typu socket_address, což je polymorfní typ zahrnující IPv4, IPv6 i řetězce (používané jako jména UNIX domain socketů). Všechny tyhle informace jsou explicitně napsané ve zdrojáku, z něj se automaticky přenesou do dokumentace (např. v C++ pomocí Doxygenu), a to všechno bez nutnosti napsat jediné slovo komentáře a jediný test zjišťující, zda se předávají nebo vrací hodnoty správných typů. Testy už píšu jen na ověření chování této funkce se smysluplnými typy parametrů.

18
Vývoj / Re:Co si myslíte o OOP?
« kdy: 05. 01. 2019, 23:15:24 »
Při chybě v programu to spadne už v testech - stejně jako při statickém typování.

Při statickém typování to nepůjde vůbec přeložit, chybu zjistíš ještě dřív, než napíšeš jedinou řádku testů.

Tohle je tady mylně považováno za výhodu statických jazyků, ale ten program nepůjde přeložit vůbec úplně celý.
Přitom v dynamickém jazyce mi ta hotová část programu může už dávno běžet...

To je zcela zásadní výhoda statických jazyků. O tom, že  to není omyl, svědčí i všechny ty snahy naroubovat statickou typovou kontrolu na původně dynamicky typované jazyky. A zatímco opravuji kód, aby prošla typová kontrola, hotová část programu, tedy předchozí zkompilovatelná, odladěná, otestovaná a v mém oblíbeném VCS uložená verze, už dávno běží v produkčním nasazení. A já se přitom nemusím bát, že se z nějakého podivného důvodu do nějaké pomocné funkce, která normálně dostává řetězce a volá na ně clear(), dostane jako parametr handle k databázi :)

19
Vývoj / Re:Co si myslíte o OOP?
« kdy: 05. 01. 2019, 23:01:10 »
Generika jsou pouze berličkou, aby se ve staticky typovaných jazycích dalo dělat totéž co v dynamicky typovaných.

Tahle věta platí pro třídu Object v Javě, nebo void* a makra v C. Naopak generika v Javě nebo šablony v C++ přidávají něco navíc - možnost definovat chování nějaké třídy, jejíž objekty se nějakým způsobem starají o objekty jiných tříd (typický příklad jsou kolekce nebo chytré ukazatele), bez znalosti typu vnitřních objektů, ale zároveň při použití takové generické třídy mít možnost omezit, co může obsahovat. Je to přesně jako v tom příkladu s Iterable<Printable> versus Iterable<Object>, mám kolekci objektů, jejíž implementace procházení obsažených objektů nezávisí na typu obsažených objektů, ale chci vynutit (staticky v době překladu), že při konkrétním použití budou ty obsažené objekty něco umět.

20
Vývoj / Re:Co si myslíte o OOP?
« kdy: 05. 01. 2019, 22:42:55 »
Tento příklad krásně ilustruje problém dynamicky typovaných jazyků. Procedura vypis() implicitně předpokládá, že parametr kolekce se dá iterovat a že iterací dostanu objekty, pro které má smysl volat print().
Toto není problém dynamicky typovaných jazyků, ale pouze tohoto příkladu. Navíc je to napsané spíš strukturovaně, než s OOP.
V OOP by pro výpis sloužila samostatná třída (nějaký Printer), s pomocí návrhového vzoru Visitor by si implementovala sama tu metodu print(), nebo by ji delegovala na ty prvky, u kterých to potřebuji. Navíc ve skutečnosti vůbec nemusí záležet na tom, zda chci vypsat kolekci, prvek, a nebo třeba rekurzivně nějaký Composite

Zrovna print() není úplně dobrý příklad, protože "nějak smysluplně vypsat" se dají vypsat hodnoty libovolného typu. Ale třeba get_peer_address() má jistě smysl pro třídu network_socket. Hůř se ale bude hledat význam takové funkce pro string nebo int a s tím mi nepomůže, když tu funkci přesunu do samostatné třídy address_extractor. Jde mi o to, že některé operace nemají pro nějaké typy smysl, buď obecně nebo v kontextu konkrétního řešeného problému. Jakákoliv runtime reakce na volání takové operace s nevhodným typem argumentu je špatně, protože v prvé řadě nemělo dojít k tomu zavolání. Tedy jedná se o chybu v programu, kterou je nutno řešit úpravou kódu. Statické typování mi takovou chybu odhalí včas a bez toho, abych musel kvůli diagnostice psát víc, než jméno typu.

21
Vývoj / Re:Co si myslíte o OOP?
« kdy: 05. 01. 2019, 21:57:16 »
V Javě bych v deklaraci formálního parametru použil Iterable<Object>. Takže vlastně stejně, jen o pár zbytečných znaků navíc.

Těch "pár zbytečných znaků navíc" definuje kontrakt mezi autorem a uživatelem funkce, který zajistí, že funkce nepůjde zavolat s parametrem, pro který nemá smysl. Jako bonus je tento kontrakt automaticky dokumentován. Navíc, kdyby se každý Object neuměl vypsat, kompilátor na to autora funkce upozorní a donutí opravit typ na něco jako Iterable<Printable>.

22
Vývoj / Re:Co si myslíte o OOP?
« kdy: 05. 01. 2019, 21:47:08 »
Kód: [Vybrat]
#!/usr/bin/env python
# -*- coding: utf-8 -*-

def vypis(kolekce):
    print(type(kolekce))
    for i in kolekce:
        print(i)
    print()

seznam = ["Alfa", 42, 42, ("Beta", "Gamma")]
mnozina = {"Alfa", 42, 42, ("Beta", "Gamma")}
vypis(seznam)
vypis(mnozina)

Jak vidíš, tak proceduře vypis() je v daném případě jedno, zda jí předhodíš seznam nebo množinu. Vypíše obojí. Pokud je budu potřebovat rozlišit, stále ještě mohu použít reflexi.

Tento příklad krásně ilustruje problém dynamicky typovaných jazyků. Procedura vypis() implicitně předpokládá, že parametr kolekce se dá iterovat a že iterací dostanu objekty, pro které má smysl volat print(). Když nebudou splněny obě tyto podmínky, funkce havaruje. Pravděpodobně vyhodí nějakou výjimku, kterou buď očekávám a musím na ni nějak zareagovat, nebo výjimka shodí celý program. Kdybych se chtěl výjimce vyhnout, musel bych v kódu nějak ošetřit chování funkce pro všechny typy, které nejsou "iterable<printable>". Ovšem ze zadání může plynout, že nemá smysl volat vypis() na takové typy. Ve staticky typovaném jazyce bych jednoduše přidal parametru kolekce typ iterable<printable> a mohl bych se spolehnout, že případné chyby odhalí kompilátor při překladu. V dynamicky typovaném jazyce můžu tento požadavek tak akorát napsat do dokumentace a případně doufat, že na každé použití této funkce někdo napíše test, že se v tom kokrétním místě vždy volá se správným typem.

23
Vývoj / Re:Maji tabulkove databaze v dnesni dobe smysl?
« kdy: 12. 09. 2018, 23:22:13 »
Mě na SQL přijde revoluční právě ten deklarativní zápis. Pošleš engine algoritmus, a on ti vyplivne výsledky. Něco podobné je možné pozorovat u LINQ, nebo GraphQL. Díky tomu se opravdu můžu oprostit od detailů práce s databází (na druhou stranu mi přijde zase škoda vzdát se pokročilejších možností) aniž by to začalo být brutálně neefektivní.
Nechápu, co tenhle odstavec měl vyjadřovat. Jestli "oprostit od detailů práce z databází" znamená, že s databází komunikuju pomocí SQL, tak to nemá s ORM nic společného. Jestli to má znamenat, že díky deklarativnímu SQL se můžu oprostit od detailů použitím ORM, tak to není pravda.

V některých svých hračkách, co jsem dělal jsem měl specializovaný DSLko, kterým jsem se dotazoval na objekty, a pokoušel jsem se to mé pseudoORM přinutit, aby generovalo SQLka, která bych napsal stejně. Celkem to šlo.
U DSL specializovaného pro nějakou konkrétní aplikaci jsem ochoten věřit, že to může generovat rozumné SQL. Ale pak to asi nebude obecně použitelné ORM.

Nebo jiný způsobem. Nette/Database to dělá tak, že dokáže zoptimalizovat i ten tvůj kód. Při prvním průchodu to provede všechny dotazy, to si nakešuje, a při dalších už to načítá hromadně. Mě ten způsob není moc sympatický, ale funguje to.

I když pominu, že ten první průchod může trvat nechutně dlouho, tak jestli při dalších průchodech už nekomunikuje s databází a čte z keše, tak to bude mít minimálně dvě další nevýhody: 1. Keš může být hodně velká; 2. Bude to fungovat správně, jen když se data v databázi nebudou měnit.

24
Vývoj / Re:Maji tabulkove databaze v dnesni dobe smysl?
« kdy: 12. 09. 2018, 20:35:48 »
Jsi si jist, že za to může to ORM?
Setkal jsem se s kódem, který ORM nepoužíval a dopadlo to stejně.

Já na ORM pohlížím jako na něco, co by mě mělo usnadnit rutinní práci. Neočekávám, že zajistí, že se nebudu střílet do nohy.

Myslel jsem, že ORM má skrývat fakt, že data jsou v relační databází a prezentovat mu je, jako by byly v nějakém úložišti objektů. Pak mi ale připadá přirozené, že programátor s objekty pracuje po jednom a explicitně přechází mezi nimi pomocí vzájemných odkazů a při tom neřeší, že každý krok znamená dotaz do databáze. Není mi moc jasné, jak by mělo ORM vypadat, aby navádělo k efektivní práci s databází a zároveň nevypadalo jako SQL přebalené do jiné syntaxe.

25
Vývoj / Re:Maji tabulkove databaze v dnesni dobe smysl?
« kdy: 12. 09. 2018, 19:17:49 »
To je to COBOLovské myšlení, které se nepodařilo vymýtit - jen převléklo kabát, a objevilo se pod jinou jmenovkou - ORM.

Nechci ORM obhajovat, ale musím :-) Principielně není problém, aby dobré ORM posílalo optimální SQLka. Problém není myšlení, ale to, že ta ORM jsou blbě napsaná. Bohužel všechna, a to dělá pak ten blbej dojem.

Připadá mi, že ORM svádí k programování ve stylu:
Kód: [Vybrat]
sum = 0
foreach a = collection_of_a()
  if test(a) then
    b = collection_of_b(a.b_id)
    if test(b) then
      sum += b.val
return sum
Není mi jasné, jak tohle dobré ORM zoptimalizuje, i když je jasné, že se to dá přepsat do jednoho SELECTu s JOINem dvou tabulek.

26
Vývoj / Re:Zapouzdření C++: Co dělám špatně?
« kdy: 10. 09. 2018, 21:11:55 »
Pro účely zpětné kompatibility se taky zavedly inline namespace. Překladač vidí všechny namespacy s číslem verze, takže manglované názvy jsou pořád stejné. Pokud se zvedne verze, tak se vytvoří nový namespace s vyšším číslem verze a ty předchozí se nechají být. A v headeru se do neverzovaného namespace nainlinuje ten s nejvyšší verzí, takže při překladu se automaticky používá nejnovější verze.

Tohle mě zajímá. To jsi v reálu někde viděl? Kde?

Tohle používá např. standardní knihovna libc++. Místo
Kód: [Vybrat]
namespace std { ... }
tam je
Kód: [Vybrat]
namespace std { inline namespace __1 { ... } }
Celé je to ještě zabalené v makrech, jméno inline namespace __1 pochází z makra _LIBCPP_ABI_VERSION.

27
Sítě / Re:6in4 na 1:1 NAT
« kdy: 18. 06. 2018, 23:34:55 »
Kdysi jsem měl podobný problém. Jednoho dne mi přestal fungovat IPv6 tunel od HE v podobné konfiguraci, tedy veřejná IP 1:1 NATovaná na privátní adresu nastavenou na vnějším rozhraní routeru. Dotazem u providera a experimentováním jsem zjistil, že provider nastadil nový CGNAT (Juniper), který nakonfiguroval tak, že příchozí pakety se dál překládaly staticky 1:1, ale pro odchozí pakety se překládal i zdrojový port na náhodně vygenerovaný. Protokoly mimo TCP a UDP (a ICMP) to zahazovalo - tedy i protokol 41. Technik providera tvrdil, že statický NAT nejde nakonfigurovat, podle supportu Juniperu je konfigurace správná a nikdo jiný si nestěžoval. Takže neměl zájem to dál řešit a nativní IPv6 je v nedohlednu: "Prozatím jsme tento projekt pozastavili pro nezájem ze strany uživatelů a finanční náročnosti na úpravu našeho maloobchodního IS. Na úrovni páteřní a velkoobchodní sítě vč. datacentra je IPv6 podporováno v plném rozsahu." Jediný funkční workaround, na který jsem přišel, je lehce obskurní konfigurace NATu na externím rozhraní mého routeru, která pro komunikaci patřící do tunelu (protokol 41 z privátní externí adresy na cílovou adresu druhého konce tunelu u HE) překládá privátní zdrojovou adresu na příslušnou veřejnou. Na CGNAT u providera tedy paket přijde už se správnou zdrojovou adresou a nic se nepřekládá ani nezahazuje.

28
Vývoj / Re:C++ wait condition vysvětlení
« kdy: 12. 12. 2017, 00:39:53 »
A k čemu je potřeba synchronizovat tím dalším mutexem přístup k datovým členům, když to už by mělo být zajištěno tím mutexem, co vstupuje, protože při volání libovolné metody by měl být locknutý, ne?

Napadají mě tři důvody:
  • Implementace interně používá pthread_mutex_t, ale interface nabízí volání wait s QMutex nebo QReadWriteLock
  • Autorovi kódu připadalo lepší/bezpečnější zařídit si zamykání interně, než se spoléhat na externě dodaný zamčený zámek.
  • Metody wakeOne() a wakeAll() nevyžadují volání se zamčeným zámkem (i když takové volání většinou není dobrý nápad). Navíc je takto kód připravený na případné přidání dalších (např. ladicích) metod, které zafungují i bez externího zámku.

29
Vývoj / Re:Dědičnost dnes
« kdy: 03. 02. 2017, 00:46:50 »
V učebnicích vypadá každý takový příklad správný a jasný. Právě protože je to jen příklad, bez kontextu. V praxi to tak jasné není a má-li někdo potřebu psát další a další učebnice a tutoriály, tak ať si jeden takový "jasný", "správný" příklad vezme a rozebere ho trošku detailněji. Protože když to udělá, tak vyjde najevo, že

Bývá celkem dobrým zvykem v úvodních výukových textech používat zjednodušení a nerozebírat detailně všechny související problémy. Jinak by to začátečníci nepochopili.

málokdy existuje jediný, správný, optimální návrh, že obvyklá situace je více možností a v první řadě je třeba mít na mysli, co vlastně chci řešit za problém, k čemu to má sloužit, čeho chci docílit a podle toho zvolit vhodný přístup. Což je přesně to, co v takovýchto učebnicových příkladech naprosto chybí. Dokonce i u takovýchto

Tak snad to je v učebnicových příkladech pro pokročilé...

Z toho textu jsem se nedozvěděl, proč zvolil autor zrovna tento model, a zajímalo by mne, jak jsi přišel na to, že je "správný". Autor to neudělal, máš příležitost udělat to za něj.

Já jsem nereagoval na tento konkrétní článek, ale na obecné tvrzení: "Další z textů, jejichž autor podlehl dojmu, že OOP stojí a leží na dědění." Přiznám se, že jsem tento článek nečetl, úvodem do OOP jsem si prošel už před nějakými 25 lety :)

Autor článku tvrdí, že bychom měli přehodnotit svůj pohled na časovou náročnost přístupu k paměti s nahodilým přístupem a uvědomit si, že v tom nejobecnějším případě není O(1), ale O(√n). Máš příležitost mi tu teď vysvětlit, v čem se mi tak co zjednoduší v praxi.

Ono praxi spíš komplikuje, ale zato je to bližší realitě. Je to jak s fyzikou - někde stačí Newtonova mechanika, jinde je potřeba obecná teorie relativity. Já ve své každodenní praxi také nepotřebuju počítat s nekonstantní cenou přístupu do paměti, ale to mě ještě neopravňuje k tvrzení, že je to blbost, kterou nepotřebuje nikdo.

Úžasné. A teď mi na základě toho dej konkrétní příklad, na kterém bude vidět, že O(1) aproximace složitosti přístupu k RAM povede k nesprávným, rozuměj příliš optimistickým výsledkům.

Příklad je v tom diskutovaném článku a nějaká měření už tu někdo v tomto vlákně uváděl. Obecně každá paměťová hierarchie s více úrovněmi cache bude dávat při O(1) aproximaci v závislosti na volbě konstanty a analyzovaném algoritmu buď příliš optimistické, nebo pesimistické výsledky.

30
Vývoj / Re:Dědičnost dnes
« kdy: 02. 02. 2017, 23:08:19 »
To jsou věci teda, člověk je jeden den totálně nasazenej v práci a pak koukne na Root a tady je takovej armageddon! Teda pánové... Obzvlášť ty motanice kolem O-notace jsou teda výživný... Do toho se už ani motat nebudu :)

Ale když člověk zrovna čeká na doběhnutí make při ladění obskurní chyby v poměrně low-level kódu v C++, tak je čtení názorů místních odborníků na teorii vyčíslitelnosti, složitosti a programovacích jazyků docela dobrý relax :)

Stran: 1 [2] 3 4