Autor Wątek: C(++) + Haskell  (Przeczytany 4425 razy)

Offline Kos

  • Użytkownik
    • kos.gd

# Maj 09, 2009, 11:47:18
Witam

Zastanawiałem się ostatnio, jak by wyglądał poważniejszy gamedev z użyciem języków funkcyjnych. Na dziś dzień znam jedynie Haskella; w inne języki kiedyś się pewnie pobawię, ale na wszystko przyjdzie czas :)).

Są ludzie, którzy próbowali pisać w Haskellu gry, powstał jakiś normalnie wyglądający FPS w 3D, ale są to raczej nieliczne przypadki.
Nie dziwię się: Oglądałem garstkę przykładów kodu na bindingach HOpenGL, które są swoją drogą naprawdę chwalone przez userów. Niemniej kod, mimo ślicznego, funkcyjnego opakowania w monady i całą resztę, nadal oscylował wokół słodkich glVertex. Do małych, prostych gierek, wizualizacji, rysowania non-realtime – czemu nie, ale nie wyobrażam sobie robienia w ten sposób nowoczesnej gry. Trudno zliczyć wszystkie punkty, w których Haskell jako język jest mocny, ale jeśli w czymś nie jest mocny, to jest to bezpośrednie zarządzanie pamięcią – a to imo jest bardzo potrzebne do efektywnego stosowania OpenGL-a.

Jak zatem to widzę? Proste – logika w Haskellu, grafika i cała reszta w C/C++ :) Przyszło mi do głowy coś takiego:

Kod: (Haskell) [Zaznacz]
data Gamestate = --(snip)
data Input = --(snip)

gameLogic :: Gamestate -> Input -> Floating -> Gamestate
gameLogic g input dt = --(snip - tutaj logika gry)

Struktury Gamestate i Input winny być "rozumiane" zarówno przez kod Haskella, jak i kod C++. Potrzebowałbym też możliwości odpalenia funkcji gameLogic z poziomu kodu C++ - i to tyle, jeśli chodzi o wymagania odnośnie integracji.

Kod C++ realizowałby pętlę główną programu oraz rendering - renderer po prostu czytałby sobie aktualny Gamestate i rysował na jego podstawie, do tego jakieś bindingi dbałyby o przesyłanie do gameLogic odpowiedniego stanu sterowania... I to w sumie wszystko, całą resztę gry można byłoby beztrosko klepać w Haskellu – taki układ wystarczyłby do wygodnego napisania prostej gry (a po rozbudowie o dostęp logiki do danych zewnętrznych, to nie tylko do prostej :), ale to, co opisałem, można uznać za "dobry początek").

Pytanie do Was: Czy binding logiki w taki sposób wydaje się Wam sensowny, zarówno od strony koncepcyjnej, jak i realizacyjnej? Czy ktoś z Was wie, jak zrealizować takie połączenie?

Jedyne, co znalazłem na ten temat, to Haskellowski interfejs Foreign Function Interface, który pozwala wywołać z poziomu Haskella funkcję z C – czyli zupełnie w drugą stronę, niż moja koncepcja, chociaż również jest to droga którą warto przemyśleć.


Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline yarpen

  • Użytkownik

# Maj 09, 2009, 13:55:41
Jaka wlasciwie bedzie przewaga takiego rozwiazania nad podpieciem Lua?

Offline Kos

  • Użytkownik
    • kos.gd

# Maj 09, 2009, 14:11:19
Nie zrozum mnie źle - nie chcę po to podpinać Haskella, by mieć po prostu logikę w skryptach. Raczej po to, by mieć ją w Haskellu :) Kod funkcyjny generalnie często bywa prostszy do napisania (= mniej czasu na kod), wolny od wielu bugów związanych z podejściem imperatywnym (= mniej czasu na kod) i łatwiejszy w utrzymaniu (= mniej czasu na kod). Wielu ludzi uważa, że programowanie funkcyjne ma wiele przewag nad klasycznym klepaniem uroczych zmiennych, ifów i pętelek – chciałbym przekonać się na własnej skórze (i podzielić się wrażeniami), jak coś takiego sprawdzi się m.in. w gamedevie.

Offline Xion

  • Moderator
    • xion.log

# Maj 09, 2009, 14:45:23
Osobiście kilka razy próbowałem podejść do języków funkcyjnych (z Haskellem na czele) i dotąd nie widzę, w czym objawia się ich wyższość. Jakoś niezbyt przekonuje mnie to, że zamiana paradygmatu "tę operację robi się tak-i-tak <tutaj wstaw skomplikowany algorytm>" na "ten obiekt ma taką-a-taką strukturę i przy pomocy takiej-a-takiej transformacji robi się z niego inny <tutaj wstaw odpowiednio skomplikowaną funkcję>" daje z miejsca duże, wymierne korzyści.

Załóżmy jednak, że się mylę i że skomplikowane zależności, operacje i algorytmy lepiej modeluje się funkcyjnie. Pytanie teraz brzmi: czy w standardowej grze logika składa się właśnie z takich dostatecznie skomplikowanych zależności, operacji i algorytmów, żeby opłacało się w to bawić? A jeśli nawet tak, to czy nie opracowano już ich wystarczająco dobrze w językach imperatywnych - na tyle dobrze, że pisanie tego wszystkiego od początku funkcyjnie zwyczajnie się nie opłaca?

Offline Kos

  • Użytkownik
    • kos.gd

# Maj 09, 2009, 15:32:10
A jeśli nawet tak, to czy nie opracowano już ich wystarczająco dobrze w językach imperatywnych - na tyle dobrze, że pisanie tego wszystkiego od początku funkcyjnie zwyczajnie się nie opłaca?

No i widzisz: gdy napiszę coś większego, to Ci będę na to mógł odpowiedzieć, bo na razie nie mogę. I już to jest dobrym powodem, bym spróbował – dolicz jeszcze moją ciekawość i masz pełny obraz rozentuzjazmowanego zapaleńca. :)



Hm... Widzę w językach funkcyjnych (takich jak właśnie Haskell, właśnie on choćby z uwagi na wszędobylski lazy evaluation) potencjał w dzisiejszych programach. Doskonale sobie zdaję sprawę, że quicksort w dwóch linijkach to jest raptem fajna ciekawostka, a nie żaden dowód na wyższość języków funkcyjnych nad jeżem. Ale co innego mi się podoba w tym podejściu.

Spójrz, jak wygląda programowanie imperatywne. Masz wszędzie w kodzie pełno klas, tworzysz dużo obiektów, każdy ze zmiennymi i takimi tam. Każda jedna zmienna odpowiada kawałkowi pamięci i każdej trzeba poświęcić uwagę. Każdą informację umieszczasz sobie w jakiejś zmiennej lokalnej lub obiekcie klasy. Nieraz poświęcasz własną cenną uwagę na klepanie jakichś metod dostępowych, getterów, setterów, bajerów, wszystko po to, by "dane i kod były w odpowiednim miejscu".
Spójrz na takie języki jak Java: Masz więcej czasu dla siebie, bo nie musisz się troszczyć o zarządzanie pamięcią – alokujesz kiedy chcesz i samo się zwolni.
W funkcyjnych jest krok dalej – w ogóle nie alokujesz, nie martwisz się o pamięć, operujesz sobie śmiało na listach nieskończonej wielkości, przez większość czasu skupiasz się na abstrakcyjnych koncepcjach. Od strony wydajnościowej również ma to swoje plusy – skompilowana aplikacja, wg mojej wiedzy, traci mniej czasu na bawienie się w odczyt i zapis pamięci – trzeba po prostu w danym momencie obliczyć X i cały dataflow rusza po tych elementach logiki, które są potrzebne. Całość w dodatku, ze względu na brak side-effectów funkcji, volatili i diabli wiedzą czego jeszcze, jest łatwa do optymalizacji i – co ważne – zrównoleglenia na wiele rdzeni bez żadnych zmian w kodzie.

Widzę tu sporo korzyści, ale za mało jeszcze mam praktyki, by móc swobodnie dyskutować na ten temat czy chociażby kogoś zachęcać. Niemniej wierzę, że to ciekawa koncepcja i chcę to wypróbować przy rzeczywistych problemach i większych projektach.

Offline revo

  • Użytkownik

# Maj 09, 2009, 15:41:44
Widzę tu sporo korzyści, ale za mało jeszcze mam praktyki, by móc swobodnie dyskutować na ten temat czy chociażby kogoś zachęcać. Niemniej wierzę, że to ciekawa koncepcja i chcę to wypróbować przy rzeczywistych problemach i większych projektach.

Są rzeczy, które robi się łatwiej funkcjonalnie, są też rzeczy, które robi się łatwiej imperatywnie -- nie ma rozwiązania idealnego ;)

Kiedyś pisałem w OCamlu grę i fizykę 2D -- Framewars. Szczerze mówiąc po tym jak już to zrobiłem, wolałbym implementować to np. w C#.

Offline yarpen

  • Użytkownik

# Maj 09, 2009, 17:51:10
Wezcie jednak pod uwage, ze nowoczesne jezyki skryptowe to swojego rodzaju hybrydy. Lua czy Python ciezko nazwac stricte imperatywnymi.

Offline blizniak

  • Użytkownik

# Maj 09, 2009, 18:40:06
delegaty i wyrażenia lambda zaczynają być obecne w większości nowoczesnych języków (także w nowym C++) ;]
imho do robienia logiki (i całej gry) najlepszy jest Python - LUA jest ciekawa, ale nie ma takich możliwości (i nie daje się tak fajnie integrować z C++)

Offline Riddlemaster

  • Użytkownik
    • Moja strona domowa

# Maj 09, 2009, 18:44:35
Cytuj
imho do robienia logiki (i całej gry) najlepszy jest Python - LUA jest ciekawa, ale nie ma takich możliwości (i nie daje się tak fajnie integrować z C++)
I dlatego wszyscy wybierają LUA :) ? Nie żartuj, LUA ma może nieco mniejsze możliwości i powaloną jak dla mnie składnię, ale problemów z integracją z C++ nie ma praktycznie żadnych. A przy tym jest szybka. I to ostatnie jest wystarczającym argumentem za jej stosowaniem.

Offline crocodile

  • Użytkownik

# Maj 09, 2009, 19:26:06
Jaka wlasciwie bedzie przewaga takiego rozwiazania nad podpieciem Lua?

Pierwsze co mi przychodzi do głowy, to że Haskell (jeśli uda się go ładnie połączyć z systemem gry) może być szybszy niż Lua.


Offline Kos

  • Użytkownik
    • kos.gd

# Maj 09, 2009, 20:18:58
Pewnie, że w każdym języku można dorzucić inlinowe deklaracje funkcji anonimowych a'la lambda, można zrobić w każdym języku odpowiedniki haskellowych 'map', 'filter' czy 'fold' (vide Python: 2 pierwsze bez zmian, trzeci jako 'reduce'). Ale nie do końca o to chodzi w programowaniu funkcyjnym – fakt, ze te konstrukcje się przydają do wielu rzeczy w imperatywnych językach, ale tu bardziej chodzi o skupienie się na łączeniu pojedynczych czystych funkcji oraz porzucenie takich dziwnych rzeczy, jak przypisania destruktywne (x=5; x += y;). Ciekawostką jest, ile zapisów do pamięci się w ten sposób oszczędza (zgaduję, że przez lazy evaluation i operacje na całych listach, więcej operacji jest na samych rejestrach).

Co do szybkości języka – porównałem kiedyś interaktywnego Haskella (GHCi) z interaktywnym Pythonem w obliczeniach. Rekurencyjne liczenie silni z iluśtam w Haskellu zajęło 3 sekundy, iteracyjne w Pythonie – 10 sekund, rekurencyjne w pythonie – przewidywalny stack overflow. :) Wnioski dwa: Po pierwsze, zwijanie rekurencji ogonowej w locie jest fajne :-); po drugie, nie jest to najmądrzejszy benchmark na świecie, ale o Haskellu (czy raczej o GHCi) świadczy raczej dobrze.
« Ostatnia zmiana: Maj 09, 2009, 20:21:02 wysłana przez Kos »

Offline mosowski

  • Użytkownik

# Maj 09, 2009, 21:04:15
Chciałem robić gierkę w Haskellu ale po niespełna dwóch tygodniach dałem sobie spokój... Haskell lubi sobie zeżreć za dużo ramu (w szczególności liby, które nie są pisane z myślą o grach, np. parsec, czy text.xml.light). Program zżerał ostatecznie 600mb ram, a było to wczytanie danych z 8mb ogre .mesh.xml, sparsowanie skryptu materiału i renderowanie vertex arraya z texturami... Może gdybym miał więcej czasu, znalazł bym sposób na obejście kłopotu, ale termin pracowni z progr.gier gonił więc dałem sobie spokój i wróciłem do C++ :P

Offline nameczanin

  • Użytkownik
    • devlog

# Wrzesień 10, 2009, 02:14:21
Jak tam Kos? Prowadziłeś ostatnio jeszcze jakieś badania w tym kierunku?

Offline Kos

  • Użytkownik
    • kos.gd

# Wrzesień 11, 2009, 00:06:06
Nie, ostatnio nie. Haskell i programowanie funkcyjne jest dla mnie taką fazą, która natchnie mnie raz na jakiś czas, wskutek czego nie przestaję o tym myśleć przez parę tygodni, a potem normalnieję. :) Programowanie funkcyjne zakorzeniło mi się w światopoglądzie jako coś, co zdecydowanie warto poznać (dla sportu - zmienia sposób patrzenia na kod), lecz co samo w sobie niekoniecznie jest najefektywniejszym sposobem rozwiązywania większości dzisiejszych problemów.
(Choć w sumie chętnie bym się przekonał, że z tym ostatnim nie mam jednak racji :D.)

Offline bies

  • Użytkownik

# Wrzesień 11, 2009, 00:34:44
(...) lecz co samo w sobie niekoniecznie jest najefektywniejszym sposobem rozwiązywania większości dzisiejszych problemów.
(Choć w sumie chętnie bym się przekonał, że z tym ostatnim nie mam jednak racji :D.)
Problem polega na tym, że z tym ostatnim masz rację. ;D