Google Chrome 136 byl prohlášen za stabilní. Nejnovější stabilní verze 136.0.7103.59 přináší řadu novinek z hlediska uživatelů i vývojářů. Podrobný přehled v poznámkách k vydání. Opraveno bylo 8 bezpečnostních chyb. Vylepšeny byly také nástroje pro vývojáře.
Homebrew (Wikipedie), správce balíčků pro macOS a od verze 2.0.0 také pro Linux, byl vydán ve verzi 4.5.0. Na stránce Homebrew Formulae lze procházet seznamem balíčků. K dispozici jsou také různé statistiky.
Byl vydán Mozilla Firefox 138.0. Přehled novinek v poznámkách k vydání a poznámkách k vydání pro vývojáře. Řešeny jsou rovněž bezpečnostní chyby. Nový Firefox 138 je již k dispozici také na Flathubu a Snapcraftu.
Šestnáctý ročník ne-konference jOpenSpace se koná 3. – 5. října 2025 v Hotelu Antoň v Telči. Pro účast je potřeba vyplnit registrační formulář. Ne-konference neznamená, že se organizátorům nechce připravovat program, ale naopak dává prostor všem pozvaným, aby si program sami složili z toho nejzajímavějšího, čím se v poslední době zabývají nebo co je oslovilo. Obsah, který vytvářejí všichni účastníci, se skládá z desetiminutových
… více »Richard Stallman přednáší ve středu 7. května od 16:30 na Technické univerzitě v Liberci o vlivu technologií na svobodu. Přednáška je určená jak odborné tak laické veřejnosti.
Jean-Baptiste Mardelle se v příspěvku na blogu rozepsal o novinkám v nejnovější verzi 25.04.0 editoru videa Kdenlive (Wikipedie). Ke stažení také na Flathubu.
TmuxAI (GitHub) je AI asistent pro práci v terminálu. Vyžaduje účet na OpenRouter.
Byla vydána nová verze R14.1.4 desktopového prostředí Trinity Desktop Environment (TDE, fork KDE 3.5, Wikipedie). Přehled novinek i s náhledy v poznámkách k vydání. Podrobný přehled v Changelogu.
Bylo vydáno OpenBSD 7.7. Opět bez písničky.
Proud dat je posloupnost dat, kde data přicházejí postupně a může jich být i nekonečně mnoho. Příkladem proudu dat je proud řádků ze souboru nebo proud z databáze s výsledky dotazu. Skládá-li se aplikace z komponent, jenž komunikují zasíláním zpráv, máme proudy zpráv mezi komponentami.
To všechno jsou příklady proudů, s kterými knihovna scalaz-stream může manipulovat.
Nicméně čtení ze souborů řeší BufferedReader
, chceme-li mít aplikaci
složenou z komponent, jenž si posílají zprávy, můžeme použít aktory z knihovny Akka.
Proč tedy používat knihovnu scalaz-stream?
Nyní si knihovnu scalaz-stream předvedeme na jednoduchém příkladu. Poté se podíváme na srovnání knihovny scalaz-stream s jinými knihovnami pro zpracování proudů dat.
Začneme programem, který vypíše řádky souboru na standardní výstup:
io.linesR("countries.txt") .to(io.stdOutLines) .run.run
Výraz io.linesR("countries.txt")
reprezentuje proud řádků ze souboru
countries.txt
. Metodou to
pošleme obsah proudu na standardní výstup.
Výraz io.linesR("countries.txt").to(
je popis toho, co se má stát.
Aby se to stalo, musíme popis spustit, což se provede dvojím voláním run
.
Některé funkce známé z kolekcí lze aplikovat i na proudy (např. filter
,
map
, collect
, zip
, take
,
++
, …).
Následující kód vypíše hlavní město České republiky (jsou-li v souboru capitals.txt
právě hlavní města zemí ze souboru countries.txt
a to navíc ve stejném pořadí):
io.linesR("countries.txt") .zip(io.linesR("capitals.txt")) .collect { case ("Czech republic", capital) => capital } .once .to(io.stdOutLines) .run.run
zip
udělá z proudu zemí a proudu hlavních měst jeden proud, jenž obsahuje dvojice
(země, hlavní město). collect
dostane proud vytvořený funkcí zip
a napřed z něj vyfiltruje dvojice tvaru ("Czech republic", capital)
,
z nichž poté vybere hlavní města.
once
je zkratka za take(1)
– z proudu vezme první prvek.
Ekvivalentní kód v Javě vypadá následovně:
try ( BufferedReader countries = new BufferedReader(new FileReader("countries.txt")); BufferedReader capitals = new BufferedReader(new FileReader("capitals.txt")) ) { String country, capital; while ((country = countries.readLine()) != null && (capital = capitals.readLine()) != null) { if (country.equals("Czech republic")) { System.out.println(capital); break; } } }
Pojďme se podívat, čím se knihovny pro zpracování proudů liší:
V Javě i jiných programovacích jazycích můžeme pracovat s proudy dat pomocí iterátorů. Na rozdíl od knihovny scalaz-stream se iterátory nepostarají o uzavírání zdrojů, to zůstane stále na programátorovi. Další nevýhodou iterátorů je, že nepodporují asynchronní proudy – například při čekání na výsledky z databáze zablokuje iterátor vlákno, v němž běží.
Iterátory mají rozhraní Iterable
.
Knihovny z rodiny reactive extensions přináší duální rozhraní Observable
,
které slouží pro asynchronní proudy. Bohužel ani Observable
se nepostará
o uvolňování zdrojů.
Navíc oddělením synchronních proudů od asynchronních proudů vzniká problém
se znovupoužitelností kódu – kód napsaný pro rozhraní Iterable
nebude fungovat s rozhraním Observable
a naopak.
Kromě toho mají obě rozhraní problém s časovou složitostí některých operací.
Například časová složitost zřetězení proudů bude záležet na uzávorkování: zřetězení uzávorkované
doleva (tj. (a ++ b) ++ c
) bude mít kvadratickou časovou složitost a zřetězení
uzávorkované doprava (tj. a ++ (b ++ c)
) bude mít lineární časovou složitost.
S knihovnou scalaz-stream na uzávorkování nezáleží,
zřetězení má vždy lineární časovou složitost.
Jak vyřešit problém s uvolňováním zdrojů? Jednoduše, nedat uživateli možnost zdroje alokovat ani uvolňovat. Příkladem je funkce
def eachLine(path: String, f: String => Unit): Unit
jenž otevře soubor path
, pro každý jeho řádek zavolá funkci f
a poté soubor zavře. Tato funkce řeší problém s uvolňováním zdrojů.
Cenou za to je ztráta kontroly –
například není možné načítání řádků předčasně zastavit,
vždy budou načteny všechny řádky souboru.
Principu, na němž je funkce eachLine
založena, se říká
inversion of control.
Na tomto principu funguje i knihovna scalaz-stream a celá řada dalších
knihoven pro zpracování proudů s automatickou správou zdrojů.
Srovnání těchto knihoven lze najít v prezentaci o knihovně
Machines,
přičemž knihovnu scalaz-stream lze považovat za vylepšené Machines.
V tomto článku jsme se podívali, co knihovna scalaz-stream nabízí. Příště se naučíme používat základní kombinátory z knihovny scalaz-stream.
Nástroje: Tisk bez diskuse
Tiskni
Sdílej:
Vsechny novejsi jazyky pouzivaji runtimeNaštěstí to tak není.
Myslis, ze by se z D mohl stat velky hrac velikosti Javy nebo aspon Pythonu? Mam na mysli komercni aplikace, web a serverside.Nevím jestli stejné velikosti (to záleží hlavně na tom, jestli se ho chytne nějaká velká firma), ale stejné užitečnosti určitě ano. Psát v D je velmi podobný pocit, jako psát v pythonu - je to dobře navržené a na podobné úrovni abstrakce. Přitom je to stále "lowlevel" systémový jazyk. Docela se mi líbí, že D si poslední dobou začínají všímat i lidi z /r/programming, viz třeba Excited About D, či The D Language: A sweet-spot between Python and C, nebo The State of D in 2015.
Musel bych přepsat D runtime (běhovou platformu podle mojí terminologie) aby to běželo ...Přepsat bys jí nemusel, jen momentálně moc nechce fungovat bez garbage collectoru. Garbage collector je jedna z věcí, o které psal Andrei, že jí v dohledné době přepíše a celkově co jsem tak četl, tak se pracuje na odproštění phobosu od něj co to jen půjde.
A D trpí také tím, že plno vlastností jazyka závisí na GC, takže není možné jen tak GC vypnout.Vážně, vlastností? Vím o tom že systémová knihovna na tom záleží, ale vlastnosti, to je pro mě novinka. Můžeš nějaké jmenovat?
To blbý runtime bych musel celí přepsat, od správy paměti až po definice primitivních operátorů. Proč? Protože OS je opravdu lepší psát bez GC. Ano, v historii byla Genera která běžela pod GC, jenomže procesorová architektura LISP machines ho podporovalo, takže to bylo rychlí.Tady je místo, kde se hádám.
A pozor, kdybych se přece jenom rozhodl ten OS s GC napsat, stejně bych musel to blbý runtime přepsat, protože GC z něčeho musí paměť čerpat. U OS je problém, že velká část samotného jádra (bez driverů) je tvořena managementem paměti (virtualizace, alokace). Takže by to vyžadovalo napsat v D bez GC dost kódu. A to by nešlo, kdyby bylo runtime závislí na GC. Takže se vracíme na začátek. MUSEL bych to celí přepsat. A to nemluvím o tom, že by se v případě GC muselo ještě dost srát s hledáním kořenů.Zde vedu nějaké argumenty, rozpaluji se, posílám odkazy a tak.
.. runtime bych musel celí přepsat .. takže to bylo rychlí .. runtime závislí na GC .. MUSEL bych to celí přepsatTeď si do tebe kopnu za prasáckou češtinu, kop, kop. Použiju taky trochu CAPSLOCK, abych tomu dodal váhu a vykřičník! Vykřičník jak hrom!!
A taková rada, klidně nesouhlas, ale aspoň o tom něco znej a nemel sračky. kdybys v tom něco lowlevel psal či se o to pokoušel, věděl bys že v aktuálním stavu je D absolutně napíču, pokud teda zrovna nemá člověk chuť se přepisovat s druntime.Místo tohohle si představ nějaký závěr a tak podobně.
Super, takže přepsat bych runtime nemusel, ale bez GC nic neběží ... Kurva, ty si řek sračku. Víš to? Asi jo, ale prostě si jen chtěl nesouhlasit.S takhle hustým přístupem si diskutuj s někým, kdo ti na to skočí. Asi jsem dospěl, nebo co, ale nebudu ztrácet čas obhajobou něčeho, co je mi napůl ukradené, ale s čím mám náhodou osobní zkušenosti. Prostě mě za to nestojíš, pokud tě zajímá, jak to fakt je, tak si to vygoogli, pokud ne, hejtuj dál, nemám náladu klesat na tvojí úroveň diskuze. Koneckonců můj problém to není.
Bezva, příště místo srazu uživatelů budeme pořádat sraz postaviček
Tak takovahle imlementace jazyka a runtime libraries je celkem slusny show stopper i pro pouziti v aplikacich, o systemovem programovanim nemluve.Já bych hlavně moc nedal na květináče, protože, jak už jsem naznačil, jeho informace nejsou úplně kvalitní a v posledním ~roce se to hodně mění.
V posledni dobe zacinam mit pocit, ze psat cokoliv co ma byt dlouhodobe udrozovano v jazycich, ktere nemaji ISO/ANSI standard je uplne na pytel, i kdyz jsou to z vyjimkou C++ spise stojate vody.Pokud ti jde o stálost, tak nemá smysl si vybírat takového nováčka, jako je D. Sice může nabízet sexy featury, ale API systémové knihovny se stále mění. Nedokážu říct, jestli je to dobře, nebo špatně - sám za sebe jsem rád, protože to znamená, že stále probíhá vývoj, než si to „sedne“ a tedy že „dospělá“ verze bude o to lepší. Já ovšem obecně preferuji rychlost vývoje před ostatními vlastnostmi, tak chápu, že to může vadit.
a v posledním ~roce se to hodně mění.Z vlakna:
Unfortunately, I doubt @nogc exception support will be ready for 2.068 (there isn't even any plans for it at the moment), and @nogc is still unusable until that's taken care of.
Pokud ti jde o stálost, tak nemá smysl si vybírat takového nováčka, jako je D. Sice může nabízet sexy featury, ale API systémové knihovny se stále mění.Takze je to na ******
a tedy že „dospělá“ verze bude o to lepší.A Godot stale neprichazi ...
Já ovšem obecně preferuji rychlost vývoje před ostatními vlastnostmi,Jako pouzitelnosti?
zvladnuti jazyka na urovni, kdy si je clovek schopen poradit s jeho slabsimi strankamiPokud je specifikace jednoznačná, dostatečně detailní a není příliš složitá.
Takze je to na ******Prostě to bude chvíli trvat. Neznamená to, že to nemůžeš používat ihned, jen že to ihned nemůžeš používat s plnou podporou phobosu (jsou i jiné knihovny)..
Stabilitou (ve smyslu změn v API, ne padání), rychlostí, do jisté míry i dokumentací.Já ovšem obecně preferuji rychlost vývoje před ostatními vlastnostmi,Jako pouzitelnosti?
A jazyk D? Pokud se neprosadil dodnes, tak se uz asi neprosadi, uz jen kvuli C++17 ci i treba Rustu.Co se mě týče, tak je to jasná volba, pokud potřebuji sáhnout po něčem kompilovaném, protože mi garbage collector ve většině věcí nevadí.
protože mi garbage collector ve většině věcí nevadí.Hlavne treba psat kompilovane programy tak, abyste ho nepotreboval, C++11 vyse vam dava plno nastroju.
Ktere jazyky se tedy kompiluji do exe bez potreby runtime?Je otázkou, čemu přesně budeme říkat runtime. Třeba některé implementace nízkoúrovňových jazyků nepotřebují prakticky nic. Například C– nebo LLVM IR. Krom toho mnoho implementací (např. GHC Haskell, OCaml, MLton, Ur/Web, …) umí přibalit runtime přímo do spustitelného souboru – runtime je často realizován jako obyčejná knihovna, takže ho stačí staticky přilinkovat.
Vsechny novejsi jazyky pouzivaji runtimeTo asi těžko, že. Nehledě na to, že ani C a C++ nejsou v pravém smyslu staré jazyky, stále se vyvíjejí.
A jestli tomu dobre rozumim, tak runtime pri c je libc, jen je nastesti v systemuNikoli, je to jen knihovna.
Aspon od Tebe bych cekal odpoved -- ktere jazyky delaji exace?Jen si počkej na odpověď mistra programovacích jazyků výše, má mnohem větší přehled. Mimochodem, jazyk a toolchain jsou dvě zcela odlišné věci, nemá smysl jazyku přisuzovat vlastnosti, které má toolchain, jazyk nevytváří binárky ani nutně neurčuje všechny vlastnosti toolchainu.
A treba na mainframech je na tom C++ stejnePřiznám se, že ani nevím, co se zde snažíš sdělit. Na mainframech běhá ledacos, takže bude většina kategorických tvrzení už z principu chybná.
Aspon od Tebe bych cekal odpoved -- ktere jazyky delaji exace?
Jazyky? Exáče? Me gusta. Třeba Python: py2exe
. ^_^
Moje testy stejneho programu ukazaly stejny vykon Javy a optimalizovaneho C.O tomto fenoménu jsem zatím slyšel jen v testech, nikdy v hodnocené reálného software, možná škoda.
S interpretem taky muzu slouzit - kolega testoval rychlost parseru v Jave (Derby) proti jednomu v assembleru. Dvojnasobne rychlejsi Java.Což má ovšem nulovou vypovídací hodnotu.
Assembleristi a ceckari se opaji blizkosti k hardware, ale unikaji jim jakekoliv struktury slozitejsi nez pole.To jistě. Když dneska začneš psát v céčku tak se ti do pozítří z hlavy vykouří všechny znalosti datových struktur.
Koukam, ze bych si tady mel zalozit blog.Tipuju, že už tu alespoň jeden máš.
burakCo to má prosím být?
nemá moc velkou logiku psát OS v něčem co má GCZahraju si dablova advokata a zeptam se: "Proc?" Kdyz si vemu klasicky mikrokernelovou architekturu, tak je absolutne jedno, jak funguji jednotlive servery a jak pracuji s pameti. Mimochodem, hromada vyzkumu v oblasti OS jde smerem, ze se upousti od low-level prace s pameti ala struct+malloc a pracuje se s objekty na vyssi urovni abstrakce, aby sla dokazovat korektnost a dalsi vlastnosti OS.
Nebudu si tu hrát na experta v OS a ani v programování, rád svuj názor zmením, poté co se dozvím nové informace :)
Dle mého není dobré mít GC v kernelu z techto duvodu:
Garbage collector si musí nejak uchovávat ukazatele na všechna alokovaná pole, což pro celý systém bude mít urcitou pametovou nárocnost a to pole musí procházet a uvolnovat - vytežuje CPU.
Pokud budete GC delat u neceho jako síte - pokud bude každý paket dynamický, mužete vyhradit 1 procesor jen na uvolnování sítového provozu.
(to asi preháním a muselo by to být velmi špatne navržené, ale tušíte kam mírím? - malé pametové (de)alokace x set/tisíc/... krát za sekundu - jde to rešit lépe, ale když má clovek možnost udelat neco rychle, nebo správne, jsou tací, kterí to udelají správne, ale urcite to nejsou všichni)
Pro více procesorový systém musíte rešit, zda má GC bežet na každém CPU samostatne, nebo zda má bežet jedno pro všechna CPU/kde a jak budou uloženy struktury pro GC. (je vubec možné udelat GC vícevláknové pro každý procesor zvlášt?)
Na to samozrejme navazuje problém se synchronizací mezi CPU, lokováním sdílených struktur.
Hlavne si ale myslím, že bez GC je to rychlejší a méne nárocné na pamet.
To je jen pár drobností které mne ted napadají.
Všechny problémy jdou samozrejme rešit, ale nevím zda není jednodušší si proste pamet (od)alokovat dle potreby.
Microsoft napsal OS v C# (singularity), ale presto nízkoúrovnové veci jsou v ASM/C.
Trošku na me Singularity pusobí jako Hurd - obojí to je spíš akademická debata než OS a obe OS vyjdou ve stejnou dobu - až zamrzne peklo :)
Omlouvám se za diakritiku - nějaká chyba ctrl+insert shift+insert.
(je vubec možné udelat GC vícevláknové pro každý procesor zvlášt?)Pokud procesory nesdílí paměť, pak ano.
Na to samozrejme navazuje problém se synchronizací mezi CPU, lokováním sdílených struktur.Pokud sdílíte paměť, tak tyto problémy nastávají i bez GC – např.
malloc
to také musí řešit.
Hlavne si ale myslím, že bez GC je to rychlejší a méne nárocné na pamet.Záleží, jak je to implementované.
Všechny problémy jdou samozrejme rešit, ale nevím zda není jednodušší si proste pamet (od)alokovat dle potreby.Jednodušší to není, nicméně v některých situacích to může být nutné.
Pokud procesory nesdílí paměť, pak ano.To má logiku :)
Pokud sdílíte paměť, tak tyto problémy nastávají i bez GC – např. malloc
to také musí řešit.
Měl jsem na mysli i to, že GC musí procházet paměťové bloky se kterými nyní pracuje jiná část systému, ten blok se může měnit, zatímco ho chce GC číst a podobné synchronizační vychytávky.
Free většinou volá přímo ten kdo vytvářel data, většinou přímo ten samý proces, takže nedochází ani k přepnutí procesů.
Pokud už uvolňuje paměť někdo jiný než ten kdo ji vytvářel, většinou ten jiný proces s alokovanými daty pracoval, takže je má v cache a nemusí je odnikud tahat.
Záleží, jak je to implementované.S tím si dovolím nesouhlasit - můžete to udělat méně náročné na procesor, ale pak to bude stát více paměti a naopak, ale nikdy to neuděláte rychlejší a zároveň méně paměťově náročné než malloc/free způsob uvolňování, pokud ty implementace budou kvalitativně stejné. Já vím, nikdy neříkej že něco nejde, protože se najde nějaký *** který neví že to nejde a udělá to, ale tady jsem si celkem jistý... [Citation needed] (sorry Randall Munroe, ale musel jsem :) )
Jednodušší to není, nicméně v některých situacích to může být nutné.Jj to je fakt, jako líný programátor bych občas dal za GC zlatý prase :)
S tím si dovolím nesouhlasit - můžete to udělat méně náročné na procesor, ale pak to bude stát více paměti a naopak, ale nikdy to neuděláte rychlejší a zároveň méně paměťově náročné než malloc/free způsob uvolňováníS GC můžete snadno sdílet části datových struktur a tím ušetřit paměť (a někdy i čas). Například s GC můžete implementovat hash consing – sdílení (strukturálně) stejných hodnot. Často se to používá v dokazovačích (implementuje se vlastní GC) a například autor dokazovače E píše, že tím ušetří 80% až 99,99% uzlů pro termy. Navíc tím ušetří i čas, neboť statistiku pro stejné termy nepočítá vícekrát.
Měl jsem na mysli i to, že GC musí procházet paměťové bloky se kterými nyní pracuje jiná část systému, ten blok se může měnit, zatímco ho chce GC číst a podobné synchronizační vychytávky.Nemusi. Protipriklad: mikrokernel. Jednotlive casti OS jsou v oddelenych pametovych prostorech, takze prochazet pametove bloky cizich casti systemu evidentne nemusis.
ale nikdy to neuděláte rychlejší a zároveň méně paměťově náročné než malloc/free způsob uvolňování, pokud ty implementace budou kvalitativně stejné.Protipriklad: Vezmi si ve smycce milionkrat free(malloc(20)) a pak to zkus zamenit za milionkrat (nejake) gc_malloc(20) + jednou full gc.
Garbage collector si musí nejak uchovávat ukazatele na všechna alokovaná pole, což pro celý systém bude mít urcitou pametovou nárocnost a to pole musí procházet a uvolnovat - vytežuje CPUTy ukazatele neni potreba uchovavat. Ona i bezna alokace ma nejakou rezii, jen to neni tak na ocich. Pocitani odkazu, ktere se bezne pouziva, je technicky vzato jen chudy pribuzny GC.
Pokud budete GC delat u neceho jako síte - pokud bude každý paket dynamický, mužete vyhradit 1 procesor jen na uvolnování sítového provozu. (to asi preháním a muselo by to být velmi špatne navržené, ale tušíte kam mírím?Ne, netusim.
Pro více procesorový systém musíte rešit, zda má GC bežet na každém CPU samostatne, nebo zda má bežet jedno pro všechna CPU/kde a jak budou uloženy struktury pro GC. (je vubec možné udelat GC vícevláknové pro každý procesor zvlášt?)Ne, nemusis. Navic muzes jit i tak daleko, ze muzes mit GC pro jednotlive moduly -- sitovani, FS, atp.
Na to samozrejme navazuje problém se synchronizací mezi CPU, lokováním sdílených struktur.To je obecny problem i bez GC.
Hlavne si ale myslím, že bez GC je to rychlejší a méne nárocné na pamet.Byly doby, kdy si lide mysleli, ze operacni system se da psat jen v ASM.
Microsoft napsal OS v C# (singularity), ale presto nízkoúrovnové veci jsou v ASM/C.Toho nizkourovnoveho kodu tam bylo pomalu.
Trošku na me Singularity pusobí jako Hurd - obojí to je spíš akademická debata než OSSingularity byl vyzkumny projekt, takze se opravdu nediv, ze kolem toho byla akademicka debata... Srovnani s Hurdem notne pokulhava.
Ty ukazatele neni potreba uchovavat.Nějak snad musíte vědět, kterou paměť máte volnou a kterou někdo používá + alespoň velikosti alokovaných polí? (něco takového má samozřejmě i malloc)
Ne, nemusis. Navic muzes jit i tak daleko, ze muzes mit GC pro jednotlive moduly -- sitovani, FS, atp.Není to pak zas o něco pomalejší? Pokud se GC musí rozhodovat jaký typ GC pro každé uvolnění použít? Nebo běží takové GC v rámci stejného vlákna/objektu? (něco na způsob contructor x destructor - jako že si každý uklízí sám po sobě)
Byly doby, kdy si lide mysleli, ze operacni system se da psat jen v ASM.Point taken :) Srovnání singularity X hurd bylo spíš myšleno jako b*bý vtip, měl jsem ještě kocovinu a přišlo mi to vtipné (očividně pouze mě) ;)
Pokud budete GC delat u neceho jako síte - pokud bude každý paket dynamický, mužete vyhradit 1 procesor jen na uvolnování sítového provozu.
(to asi preháním a muselo by to být velmi špatne navržené, ale tušíte kam mírím? - malé pametové (de)alokace x set/tisíc/... krát za sekundu - jde to rešit lépe, ale když má clovek možnost udelat neco rychle, nebo správne, jsou tací, kterí to udelají správne, ale urcite to nejsou všichni)
Tohle by mělo jít řešit tím, že na různých úrovních budu používat různé (vhodné) nástroje, ne? Síťový provoz, tahání dat z disku a spousta dalších věcí by se řešila klasicky. A jinde, kde tolik nezáleží na rychlosti a nejde o milionkrát opakované činnosti, ale naopak je tam složitější byznys logika, by se používaly objekty, GC, RAII, výjimky a různé další vymoženosti. Někdo asi řekne, že tohle má řešit až aplikační logika, ale podle mě by to bylo užitečné už v tom OS resp. na rozhraní mezi OS a aplikacemi.
Chtěl jsem se zeptat na tvrzení o časové složitosti(cituji: "Například časová složitost zřetězení proudů bude záležet na uzávorkování: zřetězení uzávorkované doleva (tj. (a ++ b) ++ c) bude mít kvadratickou časovou složitost a zřetězení uzávorkované doprava (tj. a ++ (b ++ c)) bude mít lineární časovou složitost."). Jak to myslíte?Byly myšleny dlouhé sekvence
++
. Každé ++
vytvoří nové Observable
. Čtete-li z ((a ++1 b) ++2 c) ++3 d
(++
jsem očísloval), tak prvky z a
jdou do Observable
, jenž vytvořilo ++1
, pak do Observable
, jenž vytvořilo ++2
, a nakonec do Observable
z ++3
. Takže, když těch ++
tam bude třeba milión, tak každý prvek z a
musí projít miliónem Observable
, ne?
V RxScala to simuluje následující kód:
import rx.lang.scala.Observable object HelloWorld { def leftAssoc(i: Int, acc: Observable[Int] = Observable.empty): Observable[Int] = if (i > 0) leftAssoc(i - 1, acc ++ Observable.just(i)) else acc def rightAssoc(i: Int, acc: Observable[Int] = Observable.empty): Observable[Int] = if (i > 0) rightAssoc(i - 1, Observable.just(i) ++ acc) else acc def f(i: Int): Unit = { val l = leftAssoc(i) val r = rightAssoc(i) println("i = " + i) println("startingl") val x = System.nanoTime() l.subscribe(i => (), e => (), () => ()) val x2 = System.nanoTime() println("startingr") val y = System.nanoTime() r.subscribe(i => (), e => (), () => ()) val y2 = System.nanoTime() val bil = 1000 * 1000 * 1000 println((x2-x) / bil,(y2-y) / bil) } def main(args: Array[String]): Unit = { Seq.range(1, 5).map(_ * 10000).foreach(f) } }Podle výstupu to však vypadá, že složitost je bohužel kvadratická, ať to uzávorkujete zleva nebo zprava:
i = 10000 startingl startingr (1,1) i = 20000 startingl startingr (9,9) i = 30000 startingl startingr (24,30) i = 40000 startingl startingr (68,60)Navíc jsem si musel s RxScala zvětšit velikost zásobníku, aby mi to nepadalo. Analogický kód pro scalaz-stream je:
import scalaz.concurrent.Task import scalaz.stream._ object HelloWorld { def leftAssoc(i: Int, acc: Process[Task, Int] = Process.empty): Process[Task, Int] = if (i > 0) leftAssoc(i - 1, acc ++ Process(i)) else acc def rightAssoc(i: Int, acc: Process[Task, Int] = Process.empty): Process[Task, Int] = if (i > 0) rightAssoc(i - 1, Process(i).toSource ++ acc) else acc def f(i: Int): Unit = { val l = leftAssoc(i) val r = rightAssoc(i) println("i = " + i) println("startingl") val x = System.nanoTime() l.run.run val x2 = System.nanoTime() println("startingr") val y = System.nanoTime() r.run.run val y2 = System.nanoTime() val bil = 1000 * 1000 * 1000 println((x2-x) / bil,(y2-y) / bil) } def main(args: Array[String]): Unit = { Seq.range(1, 5).map(_ * 10000).foreach(f) } }Dává to výstup
i = 10000 startingl startingr (0,0) i = 20000 startingl startingr (0,0) i = 30000 startingl startingr (0,0) i = 40000 startingl startingr (0,0)
Jinak není to nikde vyřčeno, ale pochopil jsem správně, že se scalaz-stream vám zásobník nepřetéká? Jak je to možné?Ano, nepřetéká. Obecně lze použít trampolíny a vlastní kód transformovat tak, že nebude potřebovat více než jedno volání na zásobníku. Myšlenka je jednoduchá: Funkce, které budete psát, nebudou volat žádné jiné funkce – místo toho, když budete chtít zavolat funkci, tak vrátíte nějaký objekt, který říká, co chcete zavolat. Když máte výsledek, vrátíte jiný objekt, který říká, že už nechcete nic volat. Tj. vracíte
trait ReturnVal[A] // Vracím, když jsem skončil a mám výsledek. case class Result[A](a: A) extends ReturnVal[A] // Vracím, když ještě nemám výsledek a chci zavolat další funkci, která ho možná spočte. case class Cont[A](f: () => ReturnVal[A]) extends ReturnVal[A]K tomu napíšete interpretr – např. while cyklus nebo tail rekurzivní funkci, která bude opakovaně volat funkce vracející
ReturnVal
, dokud nedostane Result
. Jediný, kdo něco volá, je tedy interpretr, takže hloubka volání je 1.
To je jeden z triků, co se tam používá – je to ve třídách Trampoline
a Task
ze scalaz. Je to také popsáno v článku Stackless Scala With Free Monads.
Bohužel důsledkem trampolín je ztráta výkonu a zvýšená alokace (alokace ReturnVal
). Navíc máte jinou konvenci volání – trampolínované funkce nejsou kompatibilní s ostatními funkcemi.
Je váš scalaz-stream kód opravdu ekvivalentní? V RxScala řetězíte Observable.just, ale v scalaz-stream Process.emptyPokud se nepletu, tak
leftAssoc(3)
, vytváří
((Observable.empty ++ Observable.just(3)) ++ Observable.just(2)) ++ Observable.just(1)resp.
((Process.empty ++ Process(3)) ++ Process(2)) ++ Process(1)což je stejné.
Ekvivalentní kód v Javě vypadá následovně:
Jaký má smysl srovnávat na jedné straně Scalu + externí knihovnu a na druhé straně čistou Javu (jen standardní knihovnu)? Pro Javu existují spousty knihoven a není důvod, proč by v ní ten kód měl vypadat složitěji.
BTW: kdo je autorem článku? Bývalo zvykem tu uvádět jména nebo přezdívky.
Jaký má smysl srovnávat na jedné straně Scalu + externí knihovnu a na druhé straně čistou Javu (jen standardní knihovnu)?Cílem bylo ukázat klasický imperativní zápis – je na něm vidět, co všechno kód ve Scale dělá (například se postará o otevření a uzavření zdrojů nebo nemusí číst soubory až do konce). Pro tento kód jsem zvolil Javu, neboť Scala standardně nemá
break
a ani speciální syntax pro automatickou správu zdrojů.
Začneme programem, který vypíše řádky souboru na standardní výstup ...
open(F, "countries.txt"); print <F>;Pokud by to teda nekdo chtel pomoci cyklu, tak:
open(F, "countries.txt"); while (<F>) { print $_; }myslim ze zustanu u perlu...