Ask YC: Game programming in Lisp - is it possible?
Dear HN,
I have been programming small games in Java for the past 3 years or so and would like to expand my horizons by learning functional programming.
Is it possible to make games using something like Lisp (or another functional language)? Would there be any benefits as opposed to OOP?
For instance: say I want to make a pacman game. In java, you just have a bunch of objects that keep their state and thus globally keep the state of the game. Then you check for collisions etc. But in Lisp, you don't have state - so would that be possible to make pacman purely functionally? If so, how? And would there be any benefits?
41 comments
[ 4.1 ms ] story [ 90.6 ms ] threadIf you wanted to make a game in Haskell, which is purely functional, then your concerns would be more appropriate.
Good libraries, particularly multimedia ones, will be very important. Chicken Scheme has a great library collection (http://www.call-with-current-continuation.org/eggs/) and works well with C. (Chicken compiles Scheme to C.) Other people here strongly recommend PLT Scheme, though I haven't personally used it much.
For learning functional programming in Lisp, I'd recommend starting with The Little Schemer. After that, The Seasoned Schemer and/or SICP.
I would also consider Lua under those circumstances. Game programming is one of its strongest areas, and IMHO it is a very practical and well-designed language. (Lua and Scheme are, by far, my favorites.) You can also do FP in Lua, though it's not really emphasized, so Scheme is better suited to learning it.
http://www.haskell.org/haskellwiki/Frag
I think part of the issue is that writing a game in Haskell is a novel challenge (it's running head-on into the weak points of the language), so the people who succeed make a big deal out of it. I'm not saying they don't deserve credit, but if they had used more appropriate language(s), the game would be judged on its own merits, you know? Similarly, I find kkrieger (http://www.theprodukkt.com/kkrieger) quite impressive. Not because of great gameplay (I don't even like first-person shooters), but because they fit so much into 96kb.
Half the original post's question is about learning functional programming. That's one of the niches where Scheme trumps almost everything else, and I also think Scheme is worth learning in general.
http://en.wikipedia.org/wiki/Abuse_(computer_game)
Any other examples?
Unfortunately they were purchased by Sony and had to stop using Lisp... I guess it's the same story as viaweb/yahoo store :-)
See : http://www.franz.com/success/customer_apps/animation_graphic... http://c2.com/cgi/wiki?LispInJakAndDaxter
Which is a little different from wanting to learn functional programming by writing a game. ;)
Start here maybe? http://shiny-dynamics.blogspot.com/2006/01/munching-on-openg...
There are also ABCL (Common Lisp) and SISC (Scheme) if you want something standard that still runs on the JVM.
Plusses:
-JVM at your disposal
-STM for concurrency (haven't used this much yet but what it offers is nice)
-built-in collection types like you'd see in perl, js, python, etc., which is a much nicer syntax over the more traditional lisp dialects for those of us who aren't hard core lisp programmers.
-nice emacs/slime integration - REPL for debugging, etc.
Minuses:
-The language is still being actively developed and while most of the core features are there, there are some things that I've found missing and some things that are still changing (have had trouble with the project/module/package heirarchy a bit, would like metadata on anonymous functions).
-Debugging can be tricky, stuff will come up from the JVM without an associated line number in the clojure code, though I've seen a patch for this online
-Complexity added by the plusses. The java interop has a lot of power but it takes some getting used to. I also came into this with no recent java experience so YMMV.
-No native OO syntax, and I didn't want to write a hybrid of java/clojure code. Still, it has structs, and the idea (I think) is to use closures as the "glue" rather than objects.
It developed primarily for use as an embedded language, so the Lua authors have kept the core very small. It's easy to use it with C, and to extend the core with libraries written in C or Lua. Many things that other languages require in the core can just be loaded as libraries. This means that (much like Scheme) it makes for a clean foundation for interesting CS projects -- LPEG (http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html) is a novel parsing/pattern matching system, there's a new library to add Erlang-style concurrency (http://concurrentlua.luaforge.net/), LuaJIT (http://luajit.org/) is a cutting edge JIT compiler, etc.
It's trivially portable to anywhere C is used, and is quite fast. It's also distributed under a BSD license, if your company wants to use their own fork internally (it's ~500k of source). You can also deliver executable files with byte-compiled Lua embedded in them.
Stylistically, it's probably closest to Python, but with more Scheme influence (tail-call optimization, coroutines, non-crippled "lambdas", etc.). It's idiomatic to make heavy use of tables, which are very similar to Python dictionaries.
The standard intro is _Programming in Lua_ (http://www.inf.puc-rio.br/~roberto/pil2/). The first version (covering Lua 5.0) of the book is free online, but there were some significant changes to the language with 5.1, particularly the module system.
It has some downsides as a Language (the community, while nice, is rather small, so sometimes documentation is sparse or scattered; it's generally assumed you're already using C or C++, so the standard libraries have gaps when using Lua on its own; the module system is still maturing (LuaRocks (http://luarocks.luaforge.net/rocks/) is an attempt to fix this); etc.), but picking any particular language always involves trade-offs. I've been quite pleased with it.
(Also, MIT license, not BSD.)
Haskell has "state", but uses monads (design patterns derived from the type system) to segregate it carefully. Clojure has state, though "variables" are immutable by default and state requires the use of special concurrency-safe features.
Ypsilon has been developed as a fundamental technology for LittleWing Pinball Construction System.
More info:
http://www.littlewingpinball.com/contents/en/ypsilon.html
Check out their website for the demo video and screenshots of the game:
http://www.gamerizon.com/
Btw, they are now hiring.
Anyway your question was more about abstract concerns, but concretely, note there are pretty decent SDL and OpenGL bindings for common lisp, and mildly optimised native compiled lisp code borders on "fast enough" for fast-paced 3D games - I mean people write fun games in Python these days, never mind lisp! even if GC is not concurrent, GC pausing is much less of a problem than people make out with a little common sense and care, - prealloc object pools before levels begin, code consless or at least cons-lightly, perhaps even go ahead and disable GC runs except at inter-level breaks...
IMO CLOS is a very nice object system for game code in an object-oriented style due to multiple dispatch (see wikipedia example):
http://en.wikipedia.org/wiki/Multiple_dispatch#Common_Lisp
http://lispbuilder.sourceforge.net/lispbuilder-sdl.html
http://common-lisp.net/project/cl-opengl/
When I last looked a while back (it might be neater now), the secret to getting cl-opengl and lispbuilder-sdl to play nice together was the following incantation, enabling use of opengl 2 shaders and stuff:
It's pretty obvious that since you can do this, a game like PacMan can also be done. I don't see any obstacle to writing 3D games. Common Lisp has been used for a huge range of kinds of application.
If you're looking for a languages with no side-effects in the same sense as in Haskell, the Clojure dialect of Lisp is well worth looking at.
http://clean.cs.ru.nl/About_Clean/Platform_Games/platform_ga...
I work only a few offices away from the Xbox developer support team, so I've heard a few war stories about game scripting. Artists and designers at one huuuuge studio (I won't mention their name) have been using Lisp for runtime tweaking of weapon, AI, and vehicle behavior (among other things) for well over a decade without even knowing that it is Lisp.
Naughty Dog used their own GOAL (Game Oriented Action Lisp) for Jak and Daxter, which you can read all about at http://www.gamasutra.com/features/20020710/white_02.htm
I think the bottom line is that you need to use raw C/C++ for the fast core graphics stuff, but once you stitch your engine together in a way that supports easy COMPOSITION, then you need a scripting language. Most studios with custom engines choose Lua or similar. Big engines like Unreal and Doom use their own. Some clever teams write their own Lisps.
There's a small thinking shift between writing a game in an imperative style and writing it in a functional style. In particular, it's smaller, I think, for games than for other kinds of applications.
In a typical game, you have a global state - a series of objects describing the properties of the game world at a certain time. You continuously update these objects, based on what input you receive from the user, and then present those changes to him, by way of video and audio rendering.
In pseudoC it would look like this :
gameState s; int time = 0;
int main() { load_resources();
}Switching to a functional language, a way of thinking about the game flow I've found useful is this :
The main loop becomes a function of two parameters : the current game state and the current inputs. From these it computes the next state. After the current simulation tick has passed, the next game state become the current one, whatever video and audio you have is updated and new inputs are gathered. It would look like this :
int main() { game_loop(load_resources(),init_inputs(),0); }
gameState game_loop(gameState currentState, gameInput inputs, int game_time) { render_video(currentState); render_audio(currentState);
}Yeah, in C it will blow your stack, but functional languages have to deal with recursion much more often, and what you see there will be optimised away to a loop and be executed in constant space.
Other things to note are that you see the state passed explicitly between functions (no relying on it inherently existing as a global) and that the shift is from modifing an object as the game progresses, to creating a new object to represent the new state of the world, and discarding the old one.
Languages, be it lisp, haskell, calm, scala, f# or python, java, c, c# (used as functional languages) are relatively easy to learn. What's hard is adapting your thinking to the strengths and limitations imposed by them.
Hope this helped.
And by the way, you won't be able to do pure functional game programming in any language. At some point you'll have to bottom out in modifying display state - whether opengl calls, or directx calls, or flash display list changes.
Writing in a functional way allows you to express algorithms more easily than you could otherwise. At any given point, while doing something functionally, you only need be concerned about the state of your inputs and your output(s). Letting go of state frees your mind from worrying about so many variables, and it allows you to think about the algorithms more easily. Of course, it may take a while to start to think about things functionally, but you'll have that any time you are learning something new.
Another benefit is it makes things easily concurrent, which is a large challenge for the game industry at large. Caveat -- make sure you know how your language/implementation deals with this, however. Erlang is much different than Common Lisp in this regard, for example (unless there is a specific CL language with threads that I don't know about).
BTW, basically every lisp you'd ever use has objects, or multimethods http://clojure.org/multimethods << basically the same thing, as far as it matters.
Thus, Lisp, ML, etc allow you to use FP whenever it is beneficial, and allow you to confine your state changes such that you won't have to worry about them nearly as much. Thus, many fewer bugs.
I cannot encourage you enough to continue to look into this. My only concern is that you will run into a lack of libraries, which clojure addresses, as everyone has already mentioned.