Objective C byla podle mne mnohem lepší vize "C s OOP", než kdy bylo C++. Bohužel doplatilo na poněkud nešťastnou syntaxi a na to, že se kolem něj nikdy doopravdy nezrodil žádný pořádný ekosystém mimo proprietárního NeXT a Apple. Byl/je tu sice projekt GnuStep, který se ale rozvíjel tak pomalu, že do elementárně použitelného stavu se dostal až v době, kdy FOSS svět už dávno najel na frameworky od GNOME a Qt/KDE.
To je věc názoru. OOP v C++ je IMO horší, než v ObjC a správa paměti je v něm minimálně stejně děravá. Spíš ještě víc, protože ObjC pokud si pamatuju mělo aspoň autorelease a scěnáře, kdy unique_ptr vůbec není unique, pořadí inicializace statických objektů a další skvosty typické pro C++ v něm nevznikaly. Netvrdím, že OOP je slovo boží, to ani omylem, ale v té době se tak považovalo a ObjC i C++ vznikly právě proto.
Jde o to, že jazyk to moc nehlídá. A pokud si to má řešit sám programátor, tak může jet rovnou klidně C pointery. A ne vždy za to může programátor - může jít o spolupráci více programátorů a problém v komunikaci, nebo využívání knihovny od jiného programátora (dokonce nějakého, který není váš zaměstnanec).
Jenže za prvé vyhnout se tomu doopravdy je mnohem složitější, než by si člověk myslel. Před pár lety jsem pracoval na statickém analyzátoru C++ kódu a nevidět to na vlastní oči formou dataflow rovnic, v životě bych si nebyl dokázal představit, jaké svinstvo může být i zdánlivě banální kód v C++. A to nemluvím o tom, jak často člověk spoléhá na undefined chování aniž by si to vůbec uvědomil a program funguje v podstatě čirou náhodou.
Hlavní problém ale vůbec není, jestli to je nebo není demence. Problém je v tom, že v C++ to v obecném případě nejde matematicky prokázat. Takže reálným řešením je buď GC, nebo statická prokazatelnost zadrátovaná přímo do jazyka jako v Rustu.
Jak který real time (soft vs hard). Myslím, že právě třeba v Drážďanech nějak ukazovali, že lze prakticky používat hard i soft real time na stejném stroji s jedním OS, lze virtualizovat a jistě lze i spouštět něco s GC - jen to holt musí být asi real time aware GC.
Další aspekt je, že GC může běžet tzv. na dluh. Program se prostě po nějaké době restartuje a tím pádem GC vlastně nikdy neproběhne. To se snad používá nebo používalo ve finančním sektoru, kde se některé kritické systémy psaly v Javě a bylo nutné, aby to fungovalo jen v obchodních hodinách na burze... přes noc si "GC" mohl klidně uklízet. :-)
Vždycky budete řešit tu hranici, kde existuje formální definice vynucená jazykem a definice, kterou si určil člověk.
Asi tak jako když v programu budu mít proměnou, která se jmenuje counter a v nějaké iteraci ho předělám na weight a dám mu typ float. Když ho nepřejmenuju, tak jsem teď porušil svou vlastní definici a jednoho dne se mi to šeredně nevyplatí.
To je to samý jako elektrikáři. Taky se dá zásuvka zapojit různě a neexistuje systém, který by matematicky spočítal, že mám zapojený barák dobře a nevyhořím. Proto se dodržují nějaké normy, třeba že černá je fáze, modrá je pracovní vodič a žlutozelená je nulák. Když tohle poruším, jsem dement a o požár si koleduju, nebo o to, že budu mít na svědomí elektrikáře, který to půjde zapojovat po mně
Unique není unique, v tom to je. Kód, kde "unique" vůbec unique není, vezme jakýkoli moderní překladač, aniž by hnul brvou a tudíž to není problém kodéra. Je to stejné, jako kdyby "špatný programátor" používal bool, který klidně může mít i jiné hodnoty, než true a false, nebo "špatný řidič" sešlápl brzdový pedál, který nemusí nutně sevřít brzdy, ale místo toho klidně třeba někdy zapne blinkry a jindy pro změnu ovládá ruční brzdu.
Problém je v jazyku. C++ je od začátku nedomyšlený, katastrofálně špatně navržený jazyk s nedostatečně definovanou sémantikou a k tomu "vylepšený" frankeinsteinskou akumulací featur, které se snaží problémy nikoli řešit, ale přeplácat, přičemž v mnoha případech prostě vůbec nefungují ani samy o sobě (viz např. koncepty v C++20 nebo právě ten unique). Natož dohromady.
Co chceš pořád dokazovat? C++ je nízkoúrovňový jazyk co poskytuje vysokoúrovňové konstrukce a zero-cost abstraction. To je to nejdůležitější - nízkoúrovňové věci obalit a používat tak, aby to neprosakovalo dál. Dokazovatelnost nic neřeší, se podívej kolik unsafe kódu je v Rust knihovnách. Rust má unsafe explicitně, C++ implicitně.
C++ není určitě na všechny projekty, ale jako jazyk tu bude ještě hodně dlouho a diskuze na rootu to nezmění :)
Zero cost abstraction nijak neodporuje prokazatelnosti, právě naopak. Např. v Rustu díky prokazatelnosti si u pointerů překladač sám odvodí ekvivalent atributů noalias, restrict, volatile apod, což umožňuje lépe optimalizovat kód. Zvlášť při monomorfizaci generických funkcí to má někdy podstatný vliv.
A právě u nízkoúrovňového kódu je ta prokazatelnost extrémně důležitá. Když se zamíchají pointery a dojde k use after free apod. v uživatelské aplikaci, tak v nejhorším případě spadne proces. Je to jistě nepříjemné, ale když k tomu dojde na úrovni jaderného ovladače, může se např. zkorumpovat celý souborový systém. Kdybych někdy nedej bože potřeboval kardiostimulátor, budu mnohem radši, když jeho mikrokontrolér bude řídit prokázaný kód spíš než kód, který psal nějaký Re4l pr0gramM3r co o sobě tvrdí, že to "umí používat".
V rustových knihovnách je spousta unsafe kódu tam, kde to volá knihovny v C. Jinak se v Rustu člověk s unsafe občas setkává u SIMD kódu a jinak v naprosto výjimečných případech. C++ nemá unsafe "implicitně", ve skutečnosti C++ vůbec nemá safe.
Rozumím, v tom máš recht. Vzpomínám Bajt, Softwarové noviny apod. z devadesátek a v některém z těch časopisů tenkrát p. Čada docela přesvědčivě obhajoval výhody ObjC. Akorát mi přijde, že ObjC je archeologie a C++, coby jazyk v zásadě od začátku "postparadigmatický" se dokázal vyvíjet a tu dobu, kdy se OOP zdálo jako FAKT dobrý nápad, se ctí přečkat. Jo, je to guláš, o tom žádná.
Jen mám takový nepříjemný pocit po letech, že Ondra Čada byl poslední, kdo skutečně (nejen) v diskusi dokázal přesvědčit, proč je objektový přístup (via ObjC+DO) lepší, a dneska z toho zbyl z velké části jen cargokult.
A musím přiznat, že mi někdo podobně výrazný chybí co se týče FP, kdo by dokázal srozumitelně vysvětlit i nezasvěceným, co to je monáda bez použití endomorfismů a hlavně _proč_ ;-)
25. 1. 2021, 01:42 editováno autorem komentáře
Tento týpek vysvětluje FP z trošku jiného pohledu. Je tedy trošku rozvláčnej, ale uvidíme, kam nakonec po pár živých přednáškách dojde (po několika iteracích si to trošku líp uspořádá): https://lispcast.com/
Ne, C++ hlavně uměl vždycky nabalovat. To, že něco pořád přidává věci odjinud a zesložiťuje je ukázka scházejícího designu.
Nebo zná dnes možná kromě autorů kompilátorů někdo kompletně C++ včetně stdlib? U C to ještě bylo do určité míry možné, u C++ si to nemyslím. Na druhou stranu mám s C++ naštěstí jen omezenou zkušenost a lidi, kteří v C++ programovali profesionálně např. pro Google mě o ohavnosti a neproduktivnosti C++ dále přesvědčují.
> C++ hlavně uměl vždycky nabalovat. To, že něco pořád přidává věci odjinud a zesložiťuje je ukázka scházejícího designu.
Došel jsem ke stejnému závěru. C++ je z podstaty nespravitelné a vývoj posledních let to jen dokazuje. Na papíře to vypadá, jakoby to bylo konečně lepší, ale ve skutečnosti je to jen složitější (způsobů jak někam poslat objekt už je snad 10).
V zásadě se dá říci, že když jazyk nemá GC, tak používá jen jinou formu obskurnosti jak to obejít (staré C++ se s tím vlastně vypořádalo celkem dobře) a nevypadá to, že by od té doby byla přijatelná náhrada.
Máte pravdu těch možností je v C++ více, je to daň za zpětnou kompatibilitu. A komunitní řízení rozvoje jazyka. Už zde ten odkaz byl, ale Bjarne (autor prvních návrhů C++) o tom napsal hezký článek (spíše knihu) https://www.stroustrup.com/hopl20main-p5-p-bfc9cd4--final.pdf
Pro psaní jakéhosi čistého C++ je nutné aplikovat guidelines např. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
Netvrdím, že je to dokonalé, zcela jistě najdete nějaké mezní případy, kdy lze prasit i za použití těchto doporučení, nebo naopak co nejde, pokud to dodržíte... ale je to jakýsi soupis best practice, které snižují riziko obtížně odhalitelné chyby a zvyšují přehlednost a udržitelnost kódu.
Je trochu nešťastné, že na úrovni překladače nejsou tyto záležitosti vymahatelné, např. nějakým přepínačem vypnout zpětnou kompatibilitu s prasáckým psaním tak, aby to nebylo možné.
Ale existují i nástroje, které napomohou dodržování tohoto vymáhat (statické analyzátory). VS na to má knihovnu, clang-tidy má také nějaké možnosti to kontrolovat.
Tedy ano C++ toho umožňuje mnoho bariéra používat dobře C++ je někde jinde než u jiných (zpravidla novějších) jazyků, ale je univerzální a má své využití.
V čem je moderní C++ lepší než např. Rust nebo na druhé straně třeba Clojure? Když chci psát úžasně rychlý kód a je mi celkem jedno, jak dlouho mi to zabere, tak chci dnes asi použít Rust, možná Haskell na určité specifické úlohy. Když chci něco rychle slepit na co jsou kvalitní knihovny, tak použiju možná Python, Go, možná Node.js. Když chci psát celkem velký, složitý software, kde ale kritická část není zas až tak výkonnostně náročná (řekněme pomalejší, ale řádově pořád na dosah Javy) a chci to celé napsat rychle a dostatečně kvalitně, tak si myslím, že s Clojure/ ClojureScriptem bude hodně těžké soupeřit. Rozhodně můžu říct, že typické OOP blboúlohy z univerzity by se v Clojure řešily několika formami a byl bych hotov.
Mimochodem, jak jste si jistý, že v reálném softwaru poznáte výkonnostní rozdíl, když je to rozumně napsané v C++ vs např. rozumně napsané např. v Javě nebo i Pythonu? Nemyslím zde nějaké HPC výpočty, ale třeba nějaký CRM nebo nějaký podobný software, který se historicky psal v C++ a potom se často přecházelo právě na Javu.
Taky by bylo dobré osvětlit, proč se o C++ nikdy nějak vážněji neuvažovalo pro vývoj v Linuxovém jádře, zatímco s Rustem to reálně vypadá, že bude možné do určitých částí kernelu posílat příspěvky v Rustu.
Z mého pohledu může aktuálně C++ být moderní jak chce. Realita je taková, že většina softwaru je napsaná v tom +- 10 let starém C++ a přepisovat to do něčeho lepšího by možná byl podobný oříšek jako napsat to celé rovnou v Rustu. Nebo např. právě Clojure tam, kde to dává smysl. (Třeba ty různé CRM, ERP apod. systémy.)
Resenim by mozna bylo byvalo :) mit co nejblize implementovane API (Frameworky) OS/X v Linuxu, protoze Apple to nevymyslel vubec spatne a verim tomu, ze by to spousta lidi pouzivala. Existuje cesky projekt https://en.wikipedia.org/wiki/Darling_(software) uz ho ale velmi dlouho nesleduji.
Mimo tema, z meho pohledu celkem zasasdni problem Linuxu v emulaci Win/OSX sveta je v absenci nekolika mechanismu v kernelu, ktere by se priblizily obema svetum. Potom by (nejen) Wine nemusel byt drbatko pravou nohou za levym uchem. Linus se k nekterym vecem kdysi davno vyjadroval v kernel mailing listu, ze to do Linuxu nepatri a nechce o tom ani slyset. Jde cele spektrum od "blbosti" typu jake adresy prijima mmap(), az po slozite veci typu nove IPC nebo event mechanismy.
Takze se to obchazi napr. ve wineserveru ... a wine 'helloworld.exe' v dusledku nabiha klidne i 5 vterin...
Myšlenka je hezká a možná i dobrá.. Ale kdyby jsi chtěl takové věci házet do kernelu tak s tím můžeš rozbít mnoho věcí a případná implentace některých mechanismů do kernelu by trvalo enormní čas a hlavně by to ve finále nebylo pořádně funkční... Je to jako házet sirku do kanystru s benzínem, prostě nevíš kdy to bouchne.
Předkládaný Wine je i lepší držet mimo kernel.
Jeden profesionální hasič mi říkal, že běžně ukazují, jak zhasnou zapálenou sirku v kanystru s benzínem. Jenom tedy musí být skoro plný a ne poloprázdný...
To hnidopišství je tu naschvál. Linux se upravuje a vyvíjí podle potřeby a hlavně podle toho, jestli to někdo využije a o tom využití ostatní přesvědčí. Potom už jde jen o kvalitu implementace. Čím víc v jádru jádra se bude kód nacházet, tím lepší musí být. Např. pro ztrátu výkonu musí být hodně dobré důvody a skoro určitě tam přibude nastavovátko, které negativní změnu aspoň zmírní. (Viz Meltdown a Spectre, kde lze opatření kernelu vypnout a získat tak zpět aspoň část výkonu.)
Takže kdyby Microsoft chtěl s Windows stavět na Linuxu, tak by se jistě prosadil a vhodné rozšíření třeba do Linuxu zapracoval. Možná by to celé nakonec vypadalo jinak, ale něco by se jistě upravilo. Nikdo jiný na tom očividně až takový zájem nemá, aby podstupoval několikaleté martyrium.
Ty event mechanismy jsou zajímavé tím, jak jdou napříč operačními systémy, například I/O Completion Port mají některé Unixy (AIX, Solaris) a — chvilka napětí — Windows. Konkrétně Linux mohl převzít implementaci Kqueue z BSD, ale nakonec si napsali vlastní chybou náhražku, takže teď třeba GCD na Linuxu vyžaduje libkqueue překládající syscally.
Odpovim najednou Jakub Lobodáš i Calculon protoze odpoved je spojena. Prave ty event mechanismy z kernelu jsou neco, co Unixu (v POSIXu) chybi. Ve spojeni s WriteFileEx to je celkem sikovne vyresena vec; nerikam ze reseni z Windows je idealni (NENI), ale princip je myslim lepsi nez POSIX.
Na youtube -myslim linux.com.au - je prednaska prave o podobnych vecech, jake je to negativni dedictvi Unixu/POSIXu.
Osobne mam mnohem radsi POSIX nez WinAPI, uz jen z duvodu jak snadno se pouziva, nicmene kdyz clovek zacne resit neco chytreho - typu ze potrebuju select()ovat na spouste zdroju vc. nejake user interakce bez pouziti threadu, tak je ta Windows cesta myslim lepsi.
Druha vec jsou treba mach ports, neco takoveho Linuxu myslim chybi. Nebo v Androidu Binders.
To máte pravdu, ale opačně než myslíte. Láďa Zajíček psal slavný článek o lepičích kódu a programátorech, a tehdy pronést něco jako "tak se koupí rychlejší procesor" nebo "sice je to pomalejší, ale dokáže v tom něco napsat i cvičená opi^H^H^Hjunior", tak už někdo přes NMT nebo prvního Dancalla objednává sanitku-
Go. V tom se pomalu nedá programovat. Samé if err != nil kam se člověk podívá, vše se řeší forem.
Porovnejte:
b := []int{1, 2, 3, 4, 5}
var a int[]
for _, v := range b {
tmp := v + 3
if tmp % 2 == 0 {
a = append(a, tmp)
}
}
a
let b = vec![1, 2, 3, 4, 5];
let a: Vec<i32> = b.iter()
.map(|x| x + 3)
.filter(|x| x % 2 == 0)
.collect();
Já tedy považuji za čitelnější to druhé. Prostě vidím co to dělá. Tamto nahoře mi zda, jaksi kostrbaté.
Tak co s tím Go všichni mají?
Spíš by mě zajímalo, jak bude který produkt udržovatelný. Přijde mi, že jsou jazyky, kde se dá rychle něco nabušit (a ještě úžasnější je, že je ten jazyk "snadno pochopitelný" a může v něm psát "skoro každý"), ale když se objeví větší problém nebo potřeba rozšíření daného produktu, z výhody se může stát nevýhoda.
Jelikož pokud se něco napíše tak, že to "nějak" drží pohromadě a "nějak" se to otestuje za aktuálních podmínek, je to poněkud jiný případ než když typový systém nebo nějaký jiný mechanismus (ale jaký jiný?) nutí vývojáře postavit systém tak, aby v něm byl jenom minimální prostor pro nejistotu. A když se dělá refaktorizace, systém opět dává bacha, aby kód neujel někam, kde vzniká prostor pro neošetřené méně běžné situace.
Ano, obecně je větší problém IO. Procesor, hlavně když má data v cache, počítá prakticky zdarma. Většina reálných problémů ale není limitovaná ani tak implementací jazyka, jako spíš hloupým algoritmem, hloupou infrastrukturou a leností/ nevědomostí programátora.
Třeba do databáze se holt musí chodit opatrně, protože databáze je holt pomalejší než psaní do nějaké mapy v paměti. Kdyby tomu tak nebylo, tak by spousta softwaru nebyla buď vůbec potřeba, nebo by byl mnohem jednodušší. Málokdo by se zabýval SQL, protože by nebylo potřeba psát složité dotazy, aby se to poskládalo šikovně v databázi tak, aby to mělo co nejmenší IO impact + aby to bylo nějak obšírně zamykáno pro integritu. Ta integrita by se nejspíš řešila nějakým COW způsobem + software transactional memory přímo v programovacím jazyce. Žádné DSL jako je SQL by nebylo už potřeba. (No a teď jsem v podstatě popsal Datomic :-))
Mám Coxovu knížku "Object Oriented Programming: An Evolutionary Approach" a je celkem poučné porovnat, jak bylo OOP popisováno tehdy a jak se učí dnes. Tehdy se celé OOP točilo kolem distribuovaných systémů (do extrému to je dovedené v knížce "Developing Business Applications with OpenStep"). Celý ten tehdejší koncept byl přitom překvapivě jednoduchý, na rozdíl od dnešních Swiftů, Rustů, Kotlinů apod.