2016. december 26., hétfő

Objektum orientált programozás hiba volt

Egy szakmának a tanulás és tanulmányozása általában elég sok időbe és energiába telik, mivel általánosan elmondható róluk, hogy nagyon nagy és változatos tudás bázisra épülnek, ami folyamatosan fejlődik és bővül. Pontosan emiatt nehéz róluk általánosan beszélni, de ebben a bejegyzésben mégis meg próbálom ezt megtenni.

Alapok megrepedése

  Majdnem minden mostani népszerű programozási nyelvnek az alapját az imperatív paradigma adja, ami nem meglepő hiszen annak idején nagyon fontos volt a gép közeli nyelvek használata annak érdekében, hogy minél jobb erőforrás kihasználást lehessen elérni. Viszont változnak az idők és ami régen jó megoldás volt az mára veszélyesé változott.
  Imperatív volt a legelső paradigma, amit megkérdőjeleztem, mivel van egy bizonyos tulajdonsága, ami egy olyan kétélű fegyver, ami jobban szeret minket megszúrni, mint magát a problémát. Itt side effect(vagyis mellék hatás)-ként ismert tulajdonságról beszélek. Mellék hatások abból a szempontból fontosak, hogy minden input/output esemény is annak számít.
Számomra a side effect mindig 2 dolgot jelenthetett:
  1. Egy függvény, aminek nincs visszatérési értéke(void)
  2. Egy olyan függvény, ami egy külső nem lokális és nem is konstans változó értékét használja
  Az első esett általában azért problémás, mivel a változtatásokat nem kapjuk vissza expliciten és általában a bemeneti paraméterekben kapjuk vissza a változtatásokat, ami miatt nehezebben tesztelhető lesz a függvény, hiszen a függvény helyett a bemeneti paramétereket kell majd tesztelni.
  A második eset pedig abból a szempontból problémás, hogy a függvény függ egy változótól, aminek az értéke bármikor módosulhat, ami azt jelenti, hogy a függvény függ a külső változón végrehajtott változásoktól és ami még rosszabb, hogy a függvény is módosítani tudja ezt a változót. Ebben az esetben még rosszabb a tesztelhetőség, mivel teszteléskor a külső változóra és annak értékeire is figyelni kell.
  Ennek a két esetnek a kombinációja pedig tragikusan komplex mellék hatásokat képes előidézni, ami a függvénynek a tesztelhetőségét is tragikusan megkárosítja.

  Ez a két esett elégé különbözőnek hangzik de mégis általában ugyanarra az eseményre lehet visszavezetni, ami nem más mint az értékmódosítás. Az értékmódosítás azért olyan veszélyes, mert minden mellékhatás erre az utasításra vezethető vissza. Például I/O műveletek is csak annyiból állnak, hogy a gép bizonyos regisztereinek megváltoztatjuk az értékeit.
  Értékmódosításnak még meg van az a kellemetlen hatása, hogy programunk futásába bevezeti az idő fogalmát, ami azt jelenti, hogy fontossá válik, hogy az adott változónk értéke adott pillanatban éppen menyi. Mivel ha adott pillanatban éppen rossz értéke van, akkor valószínűleg helytelen futást vagy hibát fog eredményezni. Másképpen úgy is lehet fogalmazni, hogy értékmódosítás miatt a változó egy állapot változóvá válik, ami azt jelenti, hogy ügyelnünk kell, hogy futás során mindig jó állapotba legyen ez a változónk, különben baj lesz.
Térjünk vissza az előző két esetünkhöz és képzeljük el, hogy nem engedjük meg az érték módosítást:
  1. Az első esetet úgy tudjuk megoldani, hogy a változtatásokat visszatérési értékként visszaadjuk. Ezáltal a magát a függvényt és annak visszatérési értékét tudjuk tesztelni a bemeneti paraméterek helyett.
  2. A második esetben megmarad a külső változóra való hivatkozás viszont értékmódosítás betiltása miatt ez a változó konstanssá változik. Emiatt pedig ismét könnyebben tesztelhető lesz a függvényünk, mivel nem kell a külső változó értékét változtatva tesztelgetnünk.
      Viszont ha mégis megszeretnék változtatni a külső változó értékét, akkor érdemes felvenni egy új függvényt, ami a változónak az új módosított értéket adja vissza és csak annyi a dolgunk, hogy ezt a új módosított értéket átadjuk bementi paraméterként annak a függvénynek, ami kéri a változó új értékét. Ebben az esetben két könnyen tesztelhető függvényünk lesz egy helyett.
  Egyik kellemes mellék hatása az értékmódosítás betiltásának, hogy függvényeink tiszták(pure) lesznek, amit azt jelenti, hogy bírnak  az úgynevezett referential transparency tulajdonsággal, ami azt jelenti, hogy két azonos függvény hívás mindig azonos eredményt fog eredményezni( f(a)==f(a) ).
  Ebből is látszik az imperatív és funkcionális programozás közötti különbség, míg funkcionális nyelvek a pure függvények használatát hangsúlyozzák, addig az imperatív nyelvek azok ellentétét az impure függvények használatát hangsúlyozzák.

  Másik kellemes hatása az értékmódosítás betiltásának, hogy nem alakulnak ki állapot változók, mivel minden érték változás pure függvények segítségével megy végbe, ami azért jó mert így a változások explicit módon mennek végbe függvények segítségével, vagyis egy bizonyos új érték elkészítését csak függvény hívásokkal tudjuk elérni úgy, hogy nem változtatjuk meg a változó eredeti értékét és ezáltal nem vezetünk be állapot változókat.

  Imperatív paradigma hibái ellenére mégis fontos, mivel vannak olyan algoritmusok és adatszerkezetek, amiket könnyebben lehet imperatívan megvalósítani. Nem is beszélve olyan helyzetekről, amikor nagyon fontos egy olyan gépközeli nyelv használata, amivel pontosan tudjuk irányítani a program futását és erőforrás használatát.
  Viszont az esetek többségében az ember jobban jár a funkcionális programozással vagy legalábbis funkcionális stílus használatával, mivel ebben az esetben nem csak jól tesztelhető, hanem könnyebben követhető kódot is tud készíteni az ember.

Egy ötletet imperatív alapokon

  Objektum orientáltság elég egyszerű ötletre épül: problémákat modellezzük le a való világból vett objektumok segítségével. Ezek az objektumok rendelkezzenek változókkal(állapottal) és függvényekkel(viselkedéssel). Az adott problémát ezek az objektumok oldják meg kommunikálva egymással. Objektum orientált paradigma egy nagyon könnyen vizualizálható és intuitív ötlet.
  Az összes népszerű objektum orientált nyelv imperatív alapokon nyugszik, ami azt jelenti, hogy az objektum orientált nyelvek örökölték az imperatív nyelvek erősségeit és annak gyengeségeit is egyúttal. Viszont maga az ötlet is bevezettet néhány új gyengeséget:
  1. Állapot robbanás
  2. Üzenet küldés hiánya
  3. Összetett adat szerkezet  
  Állapot robbanás: Állapot változók bevezetése exponenciálisan növeli a rendszer állapot terét.
Ez a fogalom a modell ellenőrzés világából származik és a lényege annyi, hogy minél több állapot változót használsz annál exponenciálisan nehezebben lesz érthető az egész programod működése.
Minden imperatív nyelvben gondot okoznak az állapot változóknak a száma, de egy objektum orientáltál nyelvben még súlyosabb a helyzet mivel az objektumok definíciójában benne van, hogy minden objektumnak van állapota és hogy ezek az objektumok egymással társalogva képesek egymás állapotait megváltoztatni.
 Egy objektum tehát tekinthető egy állapotnak, ami sok kis másik állapotból és olyan függvényekből áll, amik módosítgatják ezeket a kis állapotokat. Ez már egymagában komplexnek hangzik, de amikor hozzávesszük, hogy egy objektum orientált program pedig sok kis objektumból áll, amik egymással kommunikálva állandóan módosítgatják egymás állapotait, akkor rájövünk, hogy minden objektum orientált program egy hatalmas nagy és komplex állapot gépnek felel meg.
  Objektum orientáltság egyik legnagyobb hátránya, hogy ahelyett, hogy meggátolná az állapot változok keletkezését inkább ösztönzi azt.

  Üzenet küldés hiánya: Alan Kay fejéből pattant ki az objektum orientáltság ötlete, viszont elmondása szerint sokan félreértelmezték az ötletét, mivel az ötletének a lényege az üzenet küldés volt objektumok között. Üzenet küldést azért tartotta fontosnak, mivel ezáltal természetesen el tudtak szigetelődni az objektumok egymástól. Ehhez képest a legnépszerűbb OO nyelvek megengedték, hogy objektumok egymás publikus metódusait hívhassák meg, ami erőforrás hatékony módszer, viszont ezáltal az objektumok alig szigetelődtek el egymástól.
  Ez sajnos ahhoz vezet, hogy a program komponensek össze fognak fonódni, ami pedig ahhoz vezet, hogy egy komponens nagyon nehezen lesz módosítható anélkül, hogy a tőle függő komponenseket ne kéne megváltoztatni.

  Összetett adat szerkezet: Objektum az alapépítőköve minden objektum orientált nyelvnek, ami azért szerencsétlen mivel az objektumoknak nagyon komplexek. Gondoljunk bele, hogy egy objektum összeolvasztja az összetartozó adatokat, funkciókat és ennek tetejébe még örökölhetnek egymástól, tartalmazhatják egymást és meg is valósíthatnak valamilyen interfészt. Egy objektum gyakorlatilag egy imperatív programnak is megfeleltethető, mivel abban is ugyanúgy megtalálhatóak az összetartozó adatok és függvények. Csak objektumok esetében még tovább tudjuk bonyolítani a helyzetet objektum példányok készítésével és örökléssel.
  A helyzetet csak rontja, hogy új típusokat általában csak objektumok segítségével alkothatunk meg. Így még a legegyszerűbb típusaink is örökölni fogják az objektumoktól származó komplexitásokat.

Egyet előre kettőt hátra

  Sajnos az objektum orientált paradigmáról nem tudom elmondani ugyanazt mint az imperatív paradigmáról vagyis hogy fontos lenne. Azért nem tartom fontos, mivel a paradigma előnyeit elhomályosítja annak hátrányai és nem is ad túl sok pluszt az imperatív programozáshoz. Az egyetlen nagy különbség a kettő között, hogy az objektum orientált programozást "magasabb" szintű paradigmának tartják, mert egy nagyon komplex adat szerkezetet használ építő elemként. Objektumok alapként való használata nagyon jól hangzik, viszont gyakorlatban inkább ellenünk dolgozik, mint velünk.
  Én személy szerint azért nem szeretem az objektum orientált programozást, mivel nem látom, hogy lenne jövője a paradigmának és már programozás nyelvi kutatások sem foglalkoznak vele, mivel egyszerűen túl bonyolult, ahhoz hogy lehessen ráépíteni bármit is. Nem is beszélve, hogy formális verifikálásuk/model ellenőrzésüket is szinte lehetetlené teszi a paradigmánál érzékelhető súlyos állapot robbanás.

  Objektum orientáltság mindig az eszembe juttatja, hogy a választott szakmám még nagyon fiatal és még mindig abban a stádiumban van, hogy kitapogatja, hogy merre kéne haladnia. Ezért nem is meglepő ha enged a csábításnak és kipróbál egy ígéretesnek látszó ötletet, viszont fontos, hogy a hibákat felismerjük és kijavítsuk őket hiszen csak így tudjuk megtartani a helyes irányt és a profizmusunkat.
  Sokszor felteszem magamnak a kérdést, hogy mi lett volna ha annak idején nagyobb hangsúlyt kapott volna a funkcionális programozás: Szakmánk színvonala jobb vagy rosszabb lenne? Menyire lenne más az egyetemi oktatás? Milyen nyelvek lennének a legnépszerűbbek? Programok minősége javult volna? Formális verifikáció népszerűbb lenne? Esetleg minden ugyanolyan lenne csak más paradigmát használnánk? Megaynyi kérdés, amire sosem lesz konkrét válasz csak spekuláció.

Záró szavak

Remélem, hogy kellemes olvasni valónak fogja tartani ezt a bejegyzést az olvasó és nem veszi személyes támadásnak ha az esetleges kedvenc paradigmáját kritizálom. Ez a bejegyzés már egy ideje érlelődött bennem és csak most a szeretet ünnepén találtam meg a megfelelő időpontot egy ilyen komisz bejegyzés megszületésére. :D

2016. október 28., péntek

The walled city in your code


Walled cities have a really long history thanks to the fact that we love to kill each other for whatever reason. So city walls were a necessity from a very early history to modern times thanks to this fact. Of course a walled city also needs gates and guards who guard these gates and walls, so we feel a lot more safer. Nowadays we usual have fences, gates and security guards for the very same reason.
In this little post we will talk about when can our code feel a little bit safer.


An important lesson from Haskell

You can learn a lot from a pure functional programming language like Haskell, because it makes us view and solve problems in a different way. For example Haskell considers every IO action dangerous, because it causes side effects and side effects can cause unforeseen errors. It’s also one of the most important from Haskell is to be wary of any IO action.


The outside world

The outside world is dangerous, because you can caught a cold or get hit by a bus. The “digital” outside world also has it’s own dangers like errors, exceptions and bad inputs, which are things that programmers hate the most. Unfortunately we can’t live without the outside world, because we need to get and share information, but we also want to get rid of the dangers, so what to do?


The wall around the city

We build walls to control the madness of the outside world and also to feel a lot safer. In our case the goal of the wall is to protect us from the errors, exceptions and bad inputs. Basically it’s job is to handle any exception or error from the outside and also to validate any incoming value. This wall also needs to be robust so it won’t crash the system and it will recover from any unforeseen error.

For the wall we need a programming language, which can handle a lot of IO action, but it’s also robust against any exception or error.
The best language for this role is Erlang or Elixir(which is basically Erlang with simpler syntax). Erlang is a good fit, because it’s scalable and robust thanks to it’s lightweight green threads and the use of supervisor trees.


The city inside the wall

We are inside of the walls in our little safe place, far from the dangers of the world. Inside the wall is the ideal place to be, but it needs to be very strict to avoid causing any inside danger. We want to avoid causing any error or exception here, because it would spread like a fire through out the city. That’s why I said we need to be strict and so we need to use a programming language with strong type system that emphasis on avoiding anything that can cause side effects, exceptions or errors.

The best choices for this role is Haskell or OCaml, because both of them are strongly typed language. A strong type system is important, because it can guarantee that there won’t be any type mismatch or null exception(billion dollar mistake). They are also high level(functional) languages, which means it restricts the freedom of it’s users, which is a really good thing when you are going for correctness.

Another reason for using Haskell and Ocaml is that they both have a proof assistant based on them. Proof assistants are really important when you want to prove that your program works correctly. Proof assistants are basically really strict programming languages that can also be used for proving. These tools should be used, when you want really strong foundations for your city.


Final word

This idea describes a system where the core of the system is protected by the outer layer, so this way the system can isolate itself from the environment and the only thing that matters is the logical correctness of it’s code base. This is also why I really like this idea.


I hope you liked my first post in english. :D
Best way to enjoy Autumn is to watch a Summer music clip. :D

2016. február 6., szombat

Programozók nem tudnak bizonyitani

Ebben a kis bejegyzésben bizonyításról és annak szerepéről az informatikában lesz a téma.

Bizonyítás
  A bizonyítás nem más mint egy állítás helyességének az igazolása. Bizonyításokat a jó öreg ókori görög matematikusok találták ki és közülük is kiemelkedik Euclid munkája(Elemek).
Bizonyítások érdekessége, hogy készítésük során használhatunk bármilyen már bizonyított állítást, hiszen azoknak a helyességét már bizonyítottuk. Ezáltal a bizonyított állításokból még összetettbe állításokat tudunk megfogalmazni és bizonyítani.
  Bizonyítások szépsége, hogy megad egy biztos alapot, amire az ember építkezhet.

Félelem
  Eddigi tanulmányaim során mindig imádkoztam, hogy a matekos tárgyaknál ne legyen elvárás, hogy tudjam a bizonyításokat. Ami nem meglepő, mivel elég hosszúak voltak(tételhez képest), nehezen érthetőek és nem is értettem, hogy hogyan is csinálták őket. Nem is beszélve, hogy ha a bizonyítás nagyon informális volt, akkor a bizonyítás leírásából hiányozhatott néhány lépés, mert azok "egyértelműek" voltak(vagy csak drága volt a papír és az író takarékos akart lenni).
  Bizonyítások iránti félelmem abból fakadt, hogy egyszerűen nem értettem őket vagyis nem tudtam ,hogy hogyan kell bizonyítani. Van egy megérzésem, hogy ezzel nagyon sok kollégám küzd.

Oktatás
  A magyar egyetemek arról híresek, hogy elméletet nagyon komolyan veszik és ehhez képest eddigi tanulmányai során egyetlen olyan kurzusom se volt, ami magával a bizonyítás készítéssel foglalkozott volna. Viszont ennek ellenére rengeteg bizonyítással találkoztam.
  Lehet példákon keresztül kellet volna megértenem, hogy hogyan kell bizonyítani egy tételt, ami a magam részéről mondhatom, hogy nem vált be. Ez olyan mintha egy középiskolás osztálynak a lisp programozási nyelvet úgy próbálnám megtanítani, hogy csak a példa programok kódját adnám meg nekik.

Idő
  Bizonyítások nagyon időigényes elfoglaltságok, mivel egy bizonyítás elfogadásához kell egy matematikus, aki eldönti, hogy helyes-e a bizonyítás. Tudományos körökben egy komolyabb állítás és annak a bizonyításának elfogadásához általában 2 évet kell várnia a tudósnak és ez még a jobbik eset. Kirívó ellenpéldának itt van Mocsizuki Sinicsi esete, aki egy évtizednyi munkával bizonyította be az abc sejtést, de 3 év elteltével is csak 4 ember érti a bizonyítást.
  Ebben az esetben a folyamatot az emberi tényező teszi nagyon lassúvá. Bizonyítani csak gyakorlással lehet megtanulni, de ehhez kell egy matek tanár is, aki átnézi az általunk készített bizonyítást. Viszont a matek tanárral csak hetente 1-2-szer tudunk találkozni, így a bizonyítás kipróbálásához néha napokat kellene várni.

Megoldás
  Emberiség a történelme alatt mindig kitalálta, hogy hogyan tudná az embert kiküszöbölni a folyamatokból. Ahogy eddig is mindig, most is egy gép vonja ki az embert az egyenletből.
Mi esetünkben ez nem más mint a számítógép által segített bizonyítás avagy más néven tétel bizonyítók(proof assistant) használata.
  Tétel bizonyítók segítségével egy tételt interaktív módon tudunk bebizonyítani és a bizonyításunkat a számítógép ellenőrzi le. Így sikerült kiküszöbölni a matek tanárt és sikerült magát a bizonyítás folyamatát interaktívvá tenni, ahol a számítógép minden lépésnél visszajelzést ad, hogy éppen hogyan állunk a bizonyításban.
  Az egész folyamat nagyon hasonlít a programozáshoz, hiszen a munkafolyamat ugyan az vagyis egy nagyobb problémát kisebb egyszerűbb problémákra bontunk fel. Bizonyítások esetében  egy nagyobb bizonyítást sok kisebb bizonyított állítások segítségével építünk fel.

Motiváció
  Persze jöhet a kérdés, hogy minek tanuljon meg egy programozó bizonyítani?
Bizonyítás szépsége, hogy bármilyen állításra lehet használni őket és maguk a programok is állítások sokaságából állnak össze. Egy metódus/függvény is egy állítás, ami azt állítja, hogy bizonyos bemenet hatására bizonyos kimenetet produkál. Tétel bizonyítóval ezt az állítást tudjuk megvizsgálni, hogy ténylegesen teljesül-e.
  Ezt a folyamatot formális verifikációnak hívjuk, ahol egy specifikáció alapján tudjuk verifikálni a rendszert vagyis le tudjuk ellenőrizni, hogy a rendszer tényleg azt csinálja, amit mi akarunk.
Formális verifikáció egy nagyon nagy, érdekes és fontos területe az informatikának, amiről érdemes tudni és figyelni az onnan érkező eredményeket mint például a Compcert, ami egy formálisan verifikált C fordító.

Ajánlott irodalom
  Software Foundations: Ebben a témában ez a könyv alapműnek számít. Iszonyat mennyiségű tartalom található meg a könyveben és legjobb benne, hogy online ingyen elérhető. Még mindig olvasom a könyvet, de nagyon pozitív tapasztalataim vannak a könyvel kapcsolatban.

Zárszó
  Szoftverfejlesztés kezd olyan irányba elmozdulni, hogy a jövő szoftverei már bizonyítottam biztos alapokon fognak nyugodni. Ahogy a matematikának úgy a szoftverfejlesztésnek is kellet egy kis idő, hogy fontos szerepet kapjon benne a bizonyítás.

Ahogy harcban, úgy a programozásban is fontos a fegyver megválasztása :D

2015. április 27., hétfő

Funkcionális kirándulás

Az elmúlt hónapban végre rávettem magam, hogy belevessem magam a funkcionális programozás világába.
Úgy mondhatnám, hogy még az út elején vagyok, mert 1 hónap nem túl sokra ha csak néhány nap tanulgatja az ember, de arra bőven elég, hogy beszámoljak az élményeimről.

Persze jöhet a kérdés minek a tanulni funkcionális paradigmát, ha majdnem mindenhol objektum orientált paradigma a meghatározó. Erre a válasz pedig a fejlődés, ami programozás világában a legfontosabb tulajdonság. Eddigi tapasztalataim alapján 4 nagy mérföld kő van, amivel jó ha találkozik az ember fejlesztőként:

  1. Első Hello World program futtatása.
  2. Valamilyen OOP nyelv elsajátítsa.
  3. Nagyobb projekt fejlesztése vagy/és egy könyvtár megírása.
  4. FP nyelv megtanulása.
Belegondolva ezek a lépések számomra egymás következményei voltak. Ezt a 4. mérföldkövet azért tartom fontosnak, mert kirángat a megszokott közegből és rákényszerít, hogy új dolgokat tanuljak és a megszokott dolgokra is új szemmel nézzek.

Ezelőtt is voltak kísérleteim FP nyelv tanulásával, de valahogy mindig elbuktam vele vagy egyszerűen csak nem érdekelt tovább, de most rávettem magam hogy azért is belevettem magam.
A legfontosabb különbség a korábbi próbálkozásaimhoz képest, hogy most volt időm és egy jó könyvem a témában.

Programozási nyelvnek Haskell-t választottam, mert az egyik legnépszerűbb tisztán funkcionális nyelv és nem is beszélve, hogy a nyelv 1990-ben jelent meg, ami azt jelenti, hogy nagyon érett és stabil. A nyelv egyik erőssége, hogy 20+ év alatt össze tudtak dobni egy 1-2 könyvet. :D

Régebben próbálkoztam a Scala megtanulásával, ami egy hibrid nyelv, ami keresztezi az funkcionális és objektum orientált paradigmát, de ezzel az a gond, hogy FP megtanulása tekintetében egy rossz választás, mivel egy hibrid nyelv és nem kényszeríti ki a funkcionális paradigmát hiszen ott van az objektum orientált része. Nem is beszélve, hogy nem találtam hozzá jó tanuló anyagot.
Kipróbáltam az online Scala kurzust és a hivatalos Scala könyvet is, de egyszerűen túl unalmasak voltak és ezért hagytam is az egészet.

A jó könyv, amit találtam pedig a Learn you Haskell for good, amit már nagyon régóta végig akartam olvasni, de valahogy sosem értem rá, de mostanában végre sikerült végig olvasnom. A könyv nagyszerűsége az egyszerűsége és hogy mókás játékos módon közelíti meg a tanulás kérdését. Na és persze a legjobb benne, hogy online ingyenesen elolvasható.

Haskell közösségben különlegességnek számít ez a könyv, mivel maga a kemény magja a közösségnek nagyon tudományos szemléletű, ami szerintem nagyon szembe tűnő más nyelvekkel szemben. Aki már olvasott posztokat r/haskell subredit-en az tudja, hogy miről beszélek. Ahelyett, hogy blog posztot írnának egy nyelvi érdekességről, helyette inkább egy tudományos papírt publikálnak pdf formában, amiben persze benne van a levezetés, ami megmutatja, hogy miért is működik ez az dolog.



Bennem a könyv olvasása közben 3 érzés tudatosult Haskellel szemben:

  1. Csodálatos, hogy milyen szép és rövid megoldásai lehetnek egy adott problémának.
  2. Értem a dolgot, de akkor is elég furán működik
  3. Nem igaz, hogy 3 sor kódot nem tudok megérteni vagy 2 napja
A könyv első fele elég gyorsan elment, mivel azokat már elég jól ismertem, habár még így is meggyűlt a gondom Haskell típus rendszerének megértésével. A neheze csak ezek után jött, ami nem volt más mint a funktor-ok megértése. Azért nehéz, mert igazából ez az első alkalom, amikor az olvasót megcsapja úgy igazán a matematika szele és nem valami kellemes érzés. :D

A könyvnek nagyon jó a félépítése, mert az alapoktól indul és mindig egymásra épülve adagolja az új tudást. Közben arra is figyel, hogy megértsük, hogy miért kellet a nyelvbe az a bizonyos nyelvi elem. Egy nyelv megértésénél nagyon fontos, hogy megértsük, hogy miért van benne az, ami benne van, mert ellenkező esetben akár rosszul is használhatjuk azt a bizonyos nyelvi elemet. Ennek ellenére is biztos, hogy  fog nehéz pillanatokat átélni az olvasó, mert ez még is csak egy teljesen más paradigma.

Funkcionális paradigmát azért hívják így, mert benne minden egy funkciónak tekinthető, ahogy egy objektum orientált nyelvben pedig minden egy objektumnak tekinthető. Ez akkor eset le nekem amikor láttam, hogy az összeadás is csak egy funkció. Összeadás olyan funkció, aminek 2 bemenete van és a kimenete a két összeadott bementi érték.

Szerintem mégis a legfontosabb fogalom, amit meg kell érteni az a rekurzív funkció fogalma és annak működése.
Azért fontos, mert a nyelveben nincs alapból for vagy while ciklus, hanem helyette rekurzív függvényekkel lehet ezeket megvalósítani és ezért nem is fontos, hogy benne legyenek. Már ebből is látszik, hogy máshogy kell funkcionális nyelvekhez viszonyulni.
Rekurzív függvényeknek a lényege, hogy az adott problémát felbontsunk alap és rekurzív esettre. Ha megértjük azt, hogy hogyan kell ezt csinálni, akkor gyakorlatilag már tudjuk is, hogy mi az a rekurzív funkció.

Másik nagyon fontos dolog pedig, hogy a nyelvben minden egy kifejezés vagyis vissza kell valami adnia. Ez elég érdekes lehet, mivel objektum orientált nyelvekben utasításokat is találhatunk, amiknek nem kell visszaadni semmit és általában  inkább beállítanak valamilyen értéket. Ez azt is jelenti, hogy Haskell-ben nincs void függvény, vagyis olyan függvény, amit semmit sem ad vissza.
Másik következménye, hogy Haskell-ben nehezebb nagy függvényeket írni, mivel nincsenek utasítások és mindenek vissza kell valami értéket adnia.
Ebből következik a következő érdekesség is egyben.

Haskell utolsó általam emlitett érdekessége, hogy meglévő változó értékét nem lehet módosítani vagyis a változók módosíthatatlanok alapból. A változó módosítását ezért csak úgy lehet megoldani, hogy egy új változót kreálunk a régi alapján. Ez a módszer nem új keletű, mivel objektum orientált nyelvek nagy részében ugyanígy működik a String objektum.
Azért elsőre kicsit fura lehet, hogy a kocsi színének a megváltoztatását nem festéssel, hanem egy új autó elkészítésével érjük el.

Elég furcsának tűnhet, hogy módosítás helyett mindenből újat csinálunk, de ennek van egy nagyon jó tulajdonsága, ami a side effect mentesség. A side effect nem más, mint amikor egy globális változónak az értéke hatással van egy általunk megírt metódusra. Emiatt fordulhat elő, hogy két azonos metódus hívás különböző eredményt add vissza ha globális változóra támaszkodik. Haskell-nek pontosan az egyik célja, hogy ilyen ne fordulhasson elő és minden azonos metódus hívás azonos eredményt adjon vissza és ne legyenek benne meglepetések a programozó számára.

Ezekből is látszik, hogy Haskell kikényszeríti a programozóból, hogy a problémákat felbontsa kisebb megvalósítható részekre, függvényeket egyszerűre készítse és  hogy ezek  a megírt függvények kiszámíthatóan viselkedjenek. Ha az olvasó is ráveti magát Haskell tanulásra, akkor észreveheti, hogy a nyelv megváltoztatja azt is, hogy más nyelvekben hogyan programozunk. Az már biztos, hogy az ember ezek után már jobban oda figyel arra, hogy kerülje a side effect-et és lehetőleg egyszerű metódusokat írjon, amiknek csak egyetlen egy feladatuk van.

Funkcionális programozás ugyan azt kényszeríti ki, mint a TDD(Teszt vezérelt fejlesztés), vagyis hogy jobb és egyszerűbben tesztelhető kódot írjunk. Ezért bárkinek csak ajánlani tudom, hogy vesse bele magát bármelyik funkcionális nyelv megtanulásába, mivel segít egy jobb és szebb kód megírásában.

Bevallom őszintén elég nehéz volt megírnom ezt a bejegyzést, mert annyi mindenről lehetne mesélni még így is, hogy alig foglalkoztam Haskellel, de mégis a bejegyzés megírása után kétes érzéseim keletkeztek a nyelvvel kapcsolatban.

Egyfelől a nagyon sokat tanultam magáról a programozásról a nyelv tanulása közben és csodálom, hogy milyen röviden és elegánsan megoldásokat lehet a nyelvvel készíteni.
Viszont a kétely is feléledt bennem, hogy menyire használnám a nyelvet tényleges valós problémák megoldására vagy egyáltalán menyire lehetséges ez egyáltalán. Ami olyan nagyszerűvé teszi a nyelvet az annyira ellene is játszik elterjedése szempontjából.

Számomra Haskell olyan mint a vim. Nagyon sok időt és gyakorlást kell belefektetni, hogy igazán produktív legyen vele az ember, de akik átélték ezt a tűz keresztséget azok mondák, hogy megérte.
Én személy szerint még olyan nyelveket fogok előnybe részesíteni, ami alapból OOP, de már megjelenek bennük a funkcionális paradigmák.(Dart, Java8)
Habár úgy nézz ki, hogy gőz erővel tartunk a hibrid nyelvek világába, szóval nem lesz nehéz dolgom ilyen nyelveket találnom és használnom.

Meglepetésként pedig jöjjön egy pokoli jó szám:

2014. november 9., vasárnap

Mi a vektor Viktor?

  Újabb hosszú idő után eljött az idő, hogy újabb "tanulságos" blog bejegyzést írjak játék fejlesztés téma körében. Ahogy talán a címből kiderül vektorokról fogok mesélni, hogy micsoda és hogy mire lehet használni őket.
  Gondolom hozzám hasonlóan sokan vannak úgy, hogy amit az ember az iskola alatt tanul annak a nagy részét soha nem fogja tudni használni. Ez alól kivétel a vektor,  ha az ember játékfejlesztéssel foglalkozik. Játékfejlesztés egyik szépsége, hogy nagyon sok mindenre megtanít és megmutatja, hogy mire lehet a megtanult dolgokat használni.

Mi a vektor?
  Elég a szócséplésből és inkább térjünk rá a lényegre. A vektort gyakorlatilag 2 dolog alkotja: irány és mennyiség. Vektort gyakorlatban nyíllal szoktuk jelölni, ami egy kezdőpontból egy végpontba mutat és a menyisége pedig a nyílnak a hossza.

Na de mire jó ez a mi esetünkben?
  A játékok általánosságban akkor izgalmasak, hogyha mozognak benne a dolgok és ahogy fizikából megtanultuk, ahol mozgás van ott vektornak is kell lennie. Tehát a vektorok tökéletesek mozgások leírására.

Hogyan programozzuk ezt le?
  Legegyszerűbb dolgunk a a mennyiséggel van mivel ez egy egyszerű szám, amit el kell tárolnunk.
Mozgás esetében a mennyiség lehet például a sebesség.
Irány sem sokkal nehezebb, mivel ehhez érdemes egy egyszerű irány vektort használni, aminek a hossza 1 és a kezdő pontja a koordináta rendszer origója vagyis (0,0), ami miatt csak a végpont koordinátáit(x,y) kell eltárolnunk.

Irány vektor


WTF vektor a vektorban?
  Tudom tudom, de így lehet a legegyszerűbben elmagyarázni, hogy hogyan is kell irányt megadni.
Gyakorlatilag ez nem más mint egy iránytű, ami megmutatja, hogy egy adott játék elem milyen irányba mozog.

Aha, de hogy használom őket együtt?
  Mozgások esetében a mennyiség a sebesség lesz és az irány pedig a test mozgási iránya. Ebben az esetben csak annyi a dolgunk, hogy összeszorozzuk őket. Így pedig megkapjuk a vektort, ami gyakorlatilag elmondja, hogy a test milyen sebsebéggel mozog x és y irányban.

Triviális esetek:
Sebsebég: 300
Felfelé alkarunk haladni, akkor irányvektor: (x:0,y:1). Ezeket összeszorozva(x:0*300,y:1*300), ezt a vektort kapjuk: (x:0,y:300)
Gondolom innen már sejhető, hogy lefelé a vektor: (x:0,y:-300), balra a vektor: (x:-300,y:0) és jobbra a vektor: (x:300,y:0).

Nem triviális esetek:
  Szerintem az átlós mozgások nem triviálisak, mivel több fajta módon is be lehet állítani az irány vektort:
1. Kiszámoljuk az irányvektor végpontját az x tengely és az irányvektor közötti szög alapján. Például: cos(30) = 0.866, sin(30) = 0.5, tehát az irány vektor: (x:0.866,y:0.5)
2. Elforgatunk egy adott irányvektor, hogy az átlósan álljon.
3. Összeadunk két merőleges vektort és a keletkező vektort lezsugorítjuk, hogy a hossza 1 legyen.

Összegezve:
  Lényegében a vektorok a játékfejlesztésben tökéletesek a mozgások leírására és ezért alapvető elemei egy fizikai motornak. Vektorok egyszerűek mivel csak menyiségből és irányból állnak. Használatuk is igen egyszerű, mivel minden nyelven találhatunk vektor könyvtárakat.
  Nekem szerencsém volt mivel StageXL-ben van egy vektor osztály, amivel megtudtam valósítani a vektor irány részét és így már csak a mennyiség része maradt, ami meg triviális feladat.

Kiegészítés:
  Mozgások esetében a sebességet is ábrázolhatjuk vektorral ha a sebességet felbontjuk horizontális és vertikális sebességre. Azért jó, mert ebben az esetben is két értéket kell eltárolni, mint az irányvektor esetében. Nem is beszélve a tényleges vektor megadásához csak össze kell szoroznunk a sebesség - és irány vektort.(Ami vektorok esetében egyszerű dolog)

Egy kis példa:
  DartRocket esetében vektorokat használtam a mozgási és irányrendszerek megoldásához.
DirectionSystem: Irányokkal való munkát segíti és definiál néhány triviális irányt.
MovementSystem: Mozgások leírását és velük való munkát segíti. 

Bónusz: 

2014. augusztus 5., kedd

Streameljük az állapotokat

Ezt a bejegyzést már jó ideje megakartam már írni,de valahogy sosem vett rá a lélek.
Ebben a bejegyzésben bemutatom az állapotgépemet, amit csináltam még anno a játék motoromhoz, mert elég érdekesen csináltam meg, mivel Stream-et használtam hozzá. Előbb viszont tisztázok néhány fogalmat.


Állapotgép
Az állapotgép, ahogy a nevéből is kiderül egy olyan gép, aminek állapotai vannak. Ezek az állapotok között klasszikus értelemben van kezdő, aktuális és végállapot. Az állapotgép feladata, hogy események hatására egyik állapotból átmenjen egy másikba. A gép célja, hogy a kezdőállapotból eljusson a végállapotba.

Ez egy egyszerű kapcsolónak a diagramja.

Jól látszik az ábrából, hogy 2 állapot van, amikbe lehetünk. Kezdő állapot és végállapot nincs, de egy igazi rendszernél biztos a kikapcsolt állapot lenne a kezdő állapot. Események itt a kapcsoló használata, ami előidézi, hogy állapot váltás menjen végbe.

Aha, de mi köze van a játékfejlesztéshez?
Játékokat általánosságban elég jól elkülöníthető állapotokra lehet bontani. Gondoljunk csak arra, ahogy egy egyszerű webes játék felépül. Először fogad minket a töltési képernyő aztán következik a menü, ahol eldönthetjük merre tovább. Nem is beszélve, hogy maga a játékmenet is több szintből épülhet fel.

Ezek mind állapotnak tekinthetőek, ahol a kezdő állapot a betöltési képernyő, ahonnan végül tovább lehet jutni többi állapotba.


Stream
Aki már csinált valaha aszinkron programozást annak ismerős lehet a Stream fogalma. Stream magyarul folyamot jelent, viszont a mi esetünkben adatfolyamnak fordítanám le, mivel gyakorlatilag az a lényege, hogy adatot továbbítson folyamatosan. Mi pedig tudjuk fogni ezt az adatfolyamatot és megkapjuk belőle azokat az adatokat, amiket éppen továbbit.
Stream-eknek még van egy nagyon jó tulajdonsága, hogy bármikor meg lehet állítani és folytatni őket.
Nem is beszélve arról, hogy nagyon könnyű velük kommunikálni.

Na ennek meg mi köze van az állapotgéphez?
Valóságban nincs túl sok közük egymáshoz, viszont mégis érdemes vegyíteni őket , mert meglepően sokat tudó rendszert lehet csinálni vele minimális kód megírásával.

Mi esetünkben egy Stream egyelő lesz egy állapottal. Tehát van máris egy állapotunk, amivel tudunk kommunikálni és akkor indítjuk el vagy állítjuk meg amikor csak akarjuk.
A valóságban csak annyi dolgunk volt, hogy az állapot(State) osztállyal örököltettük a Stream osztályt és innentől kezdve csak ki kellet egészíteni, hogy tényleg úgy viselkedjen, mint egy állapot.


Állapotok kezelése
Eddig vannak az állapotaink, de ki tárolja el ezeket az állapotokat és vajon ki indítja el a kezdő állapotot vagy egyáltalán melyik a kezdő állapot?Nem is beszélve, hogy hogyan fogunk állapotokat váltani?

Kérdések megválaszolására kell csinálnunk egy állapot menedzsert, aminek egyetlen dolga, hogy ezeket a problémákat megoldja.

Tárolás mondja egy szimpla asszociatív tömb, amiben a kulcs neve az állapot neve és a kulcshoz tartozó érték pedig egy állapot.

Kezdő állapot definiálás és indítás elég könnyű, mivel csak meg kell adni, hogy melyik állapotot kezdje el hallgatni.(Ugyebár az állapot egy Stream)

Az állapot váltásnál tudnunk kell, hogy melyik állapot következik. Én ezt úgy oldottam meg hogy éppen aktuális állapot tárolja a következő állapot nevét. Név tudatában pedig tudjuk, hogy melyik állapotot hallgassuk meg következőként, viszont előtte még az aktuális állapotot kell megállítanunk.


Állapotok megvalósítása
Az állapotgép csak akkor működik, ha vannak olyan egyéni állapotok, amik öröklik azt az absztrakt állapot osztályt, amit csináltunk és megvalósítják annak az összes absztrakt metódusát.
Az én esetemben csak a create metódus megvalósítását várom el a programozótól.

Magyarázat
Az absztrakt metódus azt jelenti, hogy nincsen megvalósítása. 
Az absztrakt osztály pedig azt jelenti, hogy tartalmaz legalább 1 absztrakt metódust és az osztály nem példányosítható.


Összegezve
Megéri az állapotokat streamelni, mivel valamennyire egyszerű a megvalósítás és nagyon sok lehetőség rejlik benne. Én személy szerint a kommunikációs készségében látom a legnagyobb lehetőséget, amit jó magam még alig használtam ki.

Ha kódot is szeretnétek látni ajánlhatom a játék motorom Állapot és Állapot menedzser osztályát. 

2014. május 31., szombat

Amikor érdemes játékmotort késziteni

A mondás
A játékkészítő közösségben van egy mondás, hogy "Készíts játékot, ne játékmotort".
Szerintem ez a mondás nagyon is igaz abból a szempontból, hogy ha tényleg játékot akarsz készíteni, akkor válasz egy jól ismert játékmotort és kezd el a fejlesztést, mivel ezek az eszközök azért vannak, hogy megkönnyítésék a játékkészítést  és ugyebár mi értelme van feltalálni a kereket ismét?
Viszont itt is lehet találni kivételeket, amikor igenis el kell gondolkozni egy saját játékmotor fejlesztésén.

Mikor érdemes tehát belefogni?
 Most csak az én esetemet tudom felhozni, ami nem más mint a saját 2D HTML5 játékmotorom, amit DartRocket- nek hívok. Az én esetemben az ok egyszerű volt. Dart nyelven nem volt egy igazi játékmotor sem és én Dart-ban akartam játékokat készíteni.
Itt találtam is egy kiskaput mivel ha nincs játékmotor, akkor csinálni kell egyet. :D


Hogyan kezdődőt el?
Ha időrendben akarok menni, akkor azt kell mondanom, hogy négy időszak volt, ami meghatározó volt.

Phaser: Ebben az időszakomban a Phaser+Javascript-es HTML5-ös játék készítéssel voltam elfoglalva. Nagyon sok időt töltöttem azzal, hogy megismerkedjem azzal, hogy hogyan érdemes vele játékot készíteni. Ezáltal megláttam azokat a dolgokat amik kelletek egy játék motorhoz és ami szerintem még fontos, hogy megtanultam API dokumentációt olvasni.
Phaser időszakom végére beleuntam, hogy nem tudtam igazán szépen strukturált kódot csinálni, még olyan eszközzel sem mint Require.js.

Dart+StageXL: Phaser-ből való kiábrándulásom után úgy voltam vele, hogy megpróbálok végre csinálni Dart-tal egy játékot. Ehhez választottam a StageXL nevezetű webGL render engine-t, amiről már jó sokat hallottam és volt hozzá néhány példakód. Az indulás döcögős volt, mert sokat kellet tanulnom Dartról és StageXL-ről. Főleg az utóbbi volt a nehezebb, mert elég hiányos hozzá a dokumentáció.
Elsőként csináltam egy space invaderes játékot és néhány kísérletet, míg végül rájöttem, hogy közeleg LD29 játékkészítő kihívás. 

LD29: Azt tudni kell, hogy a legutóbbi LD óta részt akartam venni rajta, mivel nagyon megtetszett a kihívás. Tudtam, hogy kell valami különleges dolog, amivel kitűnhetek a tömegből ezért gondoltam, hogy a játékomhoz szinteket fogok csinálni. A szintekhez meg kellet egy állapot gép(state machine), ami segítségével tudok szépen egyszerűen szinteket csinálni. Végül az egész verseny ennek a kifejlesztésére ment el és az előkelő 1200 valahányadik lettem összetettben. 

LD29 után: Észrevettem, hogy a játékom egy hatalmas hack, tele copy-paste-tel és még lassan is ment vele a fejlesztés. Ezért tudtam, hogy a következő kihívásra kell csinálnom egy játékmotort, hogy ne kelljen olyan dolgokkal vesződnöm, amik csak rabolják az ember idejét.
 Tehát volt egy problémám, ami az volt, hogy Dart-nak nincs játékmotorja és én tudtam, hogy ez egy olyan probléma, amit megtudok oldani és ezért fontos problémává vált számomra, amiért el is kezdtem rajta dolgozni.

Hogyan kezdtem hozzá?
 Szerintem egy játékmotor legfontosabb része az állapot gép, mivel ahhoz, hogy a játék szép és strukturált legyen, előbb fel kell az bontani állapotokra. Szerencsére LD29 alatt meglett ez a fontos hozzávaló. 
 Egy másik fontos hozzávaló maga StageXL volt, mivel már volt egy alapom amire építhetem az egész rendszert így nem kellet nulláról kezdenem. StageXL a játékmotorom szempontjából olyan, mint Phasernek Pixi.js.(Pixi.js egy 2D webGL renderer)
 Harmadik fontos dolog maga a célkitűzés, mert nem mindegy, hogy mi az. Én ugyanazokat az elveket vallom mint Phaser-t készítő Rich, vagyis csinálni egy olyan játékmotort, ami egyszerű, ami így magában hordozza, hogy egyszerű megérteni és használni.

 Ezeket mind észben tartva kezdtem meg a fejlesztést és miután lett egy kézzel fogható változat négy dolgot csináltam:
  • Csináltam egy Github repot és feltöltöttem oda és így mindenki elérhet
  • Feltöltöttem Dart-nak a csomag kezelőjére vagyis Pub-ra
  • Csináltam egy blogot DartRocket-nek és megcsináltam az első bejegyzést
  • Bejelentettem a Dart közösségnek, hogy mit csinálok
 Szerintem az egyik legfontosabb dolog, hogy a közösségnek bejelentettem, mivel ezáltal kaptam jó visszajelzéseket és nyomás alá is helyeztem magam, hiszen ezzel azt jelentettem be hogy ezt komolyan veszem és nyugodtan várjanak el jó munkát tőlem.

Mit remélek DartRocket-től?
 Amit a legjobban remélek, hogy valamennyire ismert lesz majd és néhányan veszik a bátorságot, hogy csináljanak vele egy játékot és közben tanuljanak magáról a Dart nyelvről. Ősszintén szólva az egyik ok, amiért elkezdtem az egészet, hogy népszerűsítsem a Dart nyelvet, mivel szerintem a nyelv nagyon is megérdemli és szerintem a játékkészítés egy nagyon hatásos módja annak, hogy másokkal megismertessünk egy nyelvvel.

Napi tipp:
 Nagyon tudom ajánlani Hamming előadását: You and your research(videó)(írott).
Számomra kiderült belőle, hogy mik is az igazán fontos problémák.
Akik nem szeretnek túl sokat olvasni vagy nézni azoknak mondom, hogy az előadás végén van egy összefoglaló.