DNS4EU, tj. evropská infrastruktura služeb DNS založená na vysoce federovaném a distribuovaném ochranném ekosystému, byla spuštěna v testovacím režimu [𝕏]. Na výběr je 5 možností filtrování DNS.
Skriptovací programovací jazyk PHP (PHP: Hypertext Preprocessor, původně Personal Home Page) dnes slaví 30 let. Přesně před třiceti lety, 8. června 1995, oznámil Rasmus Lerdorf vydání PHP Tools (Personal Home Page Tools) verze 1.0.
Ve středu v 17:00 byl ve Francii zablokován přístup k PornHubu a dalším webům pro dospělé. K 17:30 došlo k nárůstu počtu registrací Proton VPN o 1 000 % [𝕏]. Dle nového francouzského zákona jsou provozovatelé těchto webů povinni ověřovat věk uživatelů prostřednictvím průkazu totožnosti nebo platební karty.
Před 32 lety, 6. června 1993, byl spuštěn první český WWW server (ještě pod TLD .cs), pro potřeby fyziků zabývajících se problematikou vysokých energií.
Střílečku Borderlands 2 lze v rámci výprodeje série Borderlands na Steamu získat zdarma napořád, když aktivaci provedete do 8. června 19:00.
Byla vydána nová verze 2.22 svobodného video editoru Flowblade (GitHub, Wikipedie). Přehled novinek v poznámkách k vydání. Videoukázky funkcí Flowblade na Vimeu. Instalovat lze také z Flathubu.
Canonical Launchpad vypíná systém správy verzí Bazaar. Vývojáři mohou své repozitáře do 1. září přemigrovat na Git.
Byla vydána nová verze 2.53.21 svobodného multiplatformního balíku internetových aplikací SeaMonkey (Wikipedie). Přehled novinek v poznámkách k vydání.
Petici za povinné zveřejnění zdrojových kódů softwaru použitých ve veřejné správě lze podepsat na ePetice.
Na Indiegogo byla spuštěna kampaň na podporu linuxového telefonu Liberux NEXX s osmijádrovým procesorem Rockchip RK3588S, 32 GB LPDDR4x RAM a 6.34″ 2400×1080 OLED displejem. Cena telefonu je 1 310 eur.
Budeme se věnovat pouze tomu, jak se instance „tříd“ chovají (metody), nikoli tomu jak „vypadají“ (položky struktur). To druhé bylo již v minulosti probíráno a možná se podrobněji objeví jako některé z dalších témat. Poznamenejme, že v Go jsou navíc definice „rozložení“ obsahu typu a popis jeho chování, ortogonální koncepty – lze je studovat i navrhovat (téměř) nezávisle na sobě. Výhradu slovem „téměř“ ponechejme sémantice typu – často je prostě význam (pro programátora) oněch dvou tváří typu spolu provázán – i když o tom kompilátoru nic neříkáme. Příklady jsou, pro lepší přiblížení se stávajícím programátorům v jiných jazycích, „opřeny“ o strukturované typy, rád bych připomenul, že v Go lze metody definovat i pro další, např. číselné typy (pojmenované).
V příkladech si prosím v řetězcových literálech místo \x25 představte znak procenta (%). Je to jen způsob jak obejít chybu 1770.
Budeme implementovat jednoduché OOP zadání: Chceme čistě abstraktní základní třídu a napsat její různé konkrétní implementace. Jak vidno, z pohledu (zjednodušené) OOP klasiky, všechny metody „třídy“ budou (muset být) virtuální.
package main import ( "fmt" ) type Base interface { M() } type T struct { i int } func (t *T) M() { fmt.Printf("ř.16: \x25#v\n", t) } type U struct { s string } func (u *U) M() { fmt.Printf("ř.24: \x25#v\n", u) } func main() { var t, u Base = &T{42}, &U{"foo"} t.M() u.M() }
Výstup
ř.16: &main.T{i:42} ř.24: &main.U{s:„foo“}
Na ř.7 definujeme (veřejné – s velkým písmenem na začátku) rozhraní a na ř.8 deklarujeme jeho jedinou metodou M. Metody veřejného rozhraní, které má být možné implementovat i v jiných modulech, musí být také veřejné, opět tedy s velkým písmenem na začátku. Celá definice rozhraní Base je v tomto případě sémanticky shodná s definicí abstraktní třídy v jiném typickém OOP jazyku.
Na ř.11 a 19 definujeme typy, které na ř.15 a 23 implementují všechny (jednu) metodu/y rozhraní Base. Díky tomu je zde sémantika shodná s „klasickým“ popisem tříd, která dědí z/jsou v typové hierarchii potomkem „třídy“ Base. Jen zde, v typickém Go OOP duchu, u typů implementujících rozhraní Base (čti např. „extends Base“), nemusíme tento fakt oznamovat. Kompilátor se pouze při typové kontrole na ř.28 ujistí, že typy T a U implementují Base a lze je tedy přiřadit do proměnných 't' a 'u'.
Na ř.29 a 30 už jen zavoláme metodu M instancí 't' a 'u' a na výstupu programu si ověříme, že vše proběhne dle předpokladu.
Rozhraní v Go mají klasickou OOP dědičnost (úplnou a vícenásobnou), viz též poslední část specifikace. Pokud je někdo zvyklý uvažovat v třídách/hierarchii tříd a potřebuje pouze virtuální metody (situace velmi blízká Javě), může je v Go nahradit pomocí rozhraní docela snadno a největší rozdíl je „záměna“ klíčových slov class vs interface, v sémantice je shoda podle mě úplná. Go programátor jen nebude vůbec mluvit o třídách, takže si s „klasickým“ OOP programátorem chvilku nebudou rozumět – i když oba vlastně budou v tomto případě dělat totéž.
Neboli „Don't repeat yourself“. Na ř.16 a 24 příkladu 1 děláme vlastně totéž. Jeden příkaz na aplikaci principu DRY asi nestačí, ale teď jde jen o schémata přístupu a řešení. Výpis (pomocný/ladící) instance, s rozlišením jak typu tak obsahu je často používaný pomocník. Nebyl by problém si napsat zcela ne-OOP funkci, která – díky Go reflection (Javaisté vědí, C++ RTTI by nám také ale v tomto případě také stačilo) – vypíše takové informace. Proč si ale zaplevelovat jmenný prostor, když to může být metoda, že? Čeho ale metoda? Rozhraní Base? Jistě by to tak být mohlo, ale to bychom si nic nového neukázali. Prostě by k metodě Base.M přibyla nějaká metoda Base.Show a tu bychom museli dvakrát implementovat pro typy T a U (a všechny další typy implementující Base). Napsat tohoto pomocníka jako statické metody T a U také moc nepomůže, opět bychom se dvakrát téměř opakovali. Nyní nastává vhodná příležitost pro Go styl dědičnosti. Řekněme, že pomocná metoda bude jen dočasná/ladící a neveřejná (to není podmínkou). Z pohledu „tříd“ T a U dědí z Base, z pohledu Go T a U implementuje Base a dědí z 'base' (zvolené jméno 'base' není ničím podstatné).
package main import ( "fmt" "path" "runtime" ) type Base interface { M() } type base struct{} func (b base) show(me interface{}) { pc, file, line, ok := runtime.Caller(1) if !ok { panic(":-/") } fmt.Printf("\x25#x \x25s.\x25d: \x25#v\n", pc, path.Base(file), line, me) } type T struct { base i int } func (t *T) M() { t.show(t) } type U struct { base s string } func (u *U) M() { u.show(u) } func main() { var t, u Base = &T{i: 42}, &U{s: "foo"} t.M() u.M() }
Výstup:
0x400deb prog.go.30: &main.T{base:main.base{}, i:42} 0x400e3b prog.go.39: &main.U{base:main.base{}, s:„foo“}
Na ř.13 definujeme typ 'base' a na ř.15 jeho metodu 'show'. Na ř.25 a 34 uvádíme 'base' jako položku struktur typu T a U. Pole bez jména, pouze s názvem typu je v Go struktuře (myšleno klíčové slovo struct) způsob, jak zdědit obsah takto děděného typu a současně i jeho metody. Alternativně by šlo totéž i s ukazatelem (viz diskuzi „“anonymous field“), ale to nyní pomineme. Oproti OOP klasice ale přijímač (obvykle prostě ukazatel) metody takového předka má typ pouze onoho předka, nikoli typu, který jej dědí. Proto na ř.30 a 39 metodě show předáváme i přijímač metody M.
Další „standardní situací“ v OOP je případ, kdy dědíme téměř všechno od předchůdce až na jednu nebo jen několik málo metod. Ta/ty pak nějak modifikují chování zděděné/ých metod/y Zcela schematicky to v Go lze řešit takto:
package main type base struct{} func (b *base) m1() { println("m1()") } func (b *base) m2() { println("m2()") } type successor struct { base } func (o *successor) m2() { println("taky m2(), ale jinak") } func main() { b, s := &base{}, &successor{} println("typ base") b.m1() b.m2() println("typ successor") s.m1() s.m2() }
typ base m1() m2() typ successor m1() taky m2(), ale jinak
Na ř.3 definujeme základní typ/předchůdce 'base', na ř.5 a 9 jeho metody m1 a m2. Na ř.14 definujeme odvozený typ/následníka 'successor' a na ř.15 dědíme vše z předchůdce 'base'. Na ř.18 předefinováváme metodu m2, samozřejmě pouze pro typ 'successor'. Z výstupu programu vidno, že dle očekávání typ následníka podědil metodu m1, ale používá vlastní metodu m2. Mimochodem, pokud bychom v této 'overriden' metodě potřebovali volat metodu m2 předchůdce, (jako v Javě pomocí super), napsali bychom třeba před ř.19 „o.base.m2()“. Možná si to budete chtít vyzkoušet v Go playgroundu. U všech příkladů stačí klepnout na odkaz „Upravit/Spustit“.
Dnešní díl byl určen hlavně programátorům, kteří jsou navyklí na implementaci OOP mechanismů ve stylu C++ a/nebo Javy. Go je OOP jazyk, ale právě jmenovaní mají často při prvním pohledu na Go pocit, že jsou ztraceni, protože v Go nenacházejí svoje oblíbené, protože skoro neustále používané, třídy a jejich dědičnost. Pokusili jsme se ukázat, že většinu dobrého, co lze v OOP najít, Go implementuje, jen tak trochu jinak. Jestli možná v něčem i snad o trochu lépe nechť posoudí laskavý čtenář sám.
Jan Mercl, autor textu, pracuje v Laboratořích CZ.NIC jako programátor pro výzkum a vývoj.
Nástroje: Tisk bez diskuse
Tiskni
Sdílej:
def add(a:Int, b:Int) = a + bdynamický python:
def add(a, b): return a + bdynamický scheme:
(define (add a b) (+ a b))statický F#:
let add a b = a + bna scale mi vadí hlavne ten balast zbytočné zátvorky a aj zložené zátvorky označujúce začiatok a koniec bloku, ktoré len zneprehľadňujú kód, tektiež type inference v scale nefunguje najoptimálnejšie pretože u argumentov funkcií musím tak či tak zadávať názvy typov, pritom by si ich mohol prekladač automaticky odvodiť.
(1 to 100).map{i => i * i}.filter{i => (i % 2) == 0} psát třeba (1 to 100) .map i => i * i .filter i => (i % 2) == 0nebo jak si to vlastně představuješ? U argumentů funkcí typy dovodit nejdou, protože při překladu dané funkce nevíš, ze kterých všech modulů bude zavolaná. Nebrání to ovšem dělat generické funkce
scala> def joinStrings[T](x: T, y: T) = {x.toString + y.toString} joinStrings: [T](x: T, y: T)java.lang.String a tady už inference typů argumentů funguje, místo scala> joinStrings[Int](1, 2) klidně stačí napsat scala> joinStrings(1, 2) res0: java.lang.String = 12Jinak jo, staticky typovaný jazyk může působit úsporně :)
let add a b = a + bak funkcii add priradím 2 parametre rovnakého typu tak si z nich prekladač automaticky odvodí typy argumentov... ak ale jednému argumentu priradím trebárs string a druhému trebárs integer tak prekladač vyhodí chybu. rovnako prekladač vyhodí chybu aj ked 2x zavolám funkciu v vždy s iným typom argumentov.
jazyk se statickým typováním vždycky bude ukecanýrekl clovek, ktery v zivote neslysel o Haskellu...
protože je to takové C++ done right.To mi připomíná Linusovu hlášku o tom, že SVN je "CVS done right" ...
Btw, "CVS dnone right" není Linusova hláška, ale přímo moto Subversion. A je to dost přesné, SVN je oproti CVS poklad, ale v porovnání s Gitem neobstojí. Proto se tomu Linus posmíval."Posmíval" není asi to pravé. Ta citace zní: if you start with that kind of slogan, there's nowhere you can go. There is no way to do CVS right. Což je přesně jak já to vidím s C++, není jak ho "udělat dobře", špatné jsou už ty koncepty za tím. Nejlepší je se na to kompletně vykašlat a udělat to celé jinak, nejlépe přesně naopak. (Analogicky k Take CVS as an example of what not to do; if in doubt, make the exact opposite decision.)
zrovna to D je perfektní jazykZa prvé nic takového neexistuje - existují jen jazyky které jsou nepoužitelné, a méně nepoužitelné... a za druhé, který případ nastal z těchto dvou se uvidí až tak za 10 let, jak u Go, tak u D. (U C++ už se vidí :P )
Což je přesně jak já to vidím s C++, není jak ho "udělat dobře", špatné jsou už ty koncepty za tím.to ze necemu nerozumis neznamena ze je to spatne
to ze necemu nerozumis neznamena ze je to spatneTo samozřejmě ne, ale oba jevy najednou nastat můžou. A to je právě případ C++.
mám jednu otázku na autora článku:
panic("<img alt="" class="emo" src="/images/smile/smutek.gif" />")
ako prekladač jazyka zistí kde končí reťazec keď aj vo vnúti reťazca sa používa znak ukončenia reťazca bez escapovania?