• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
  • entries
    625
  • comments
    1446
  • views
    1006395

Epoch Error Handling, Part 2

Sign in to follow this  
Followers 0
ApochPiQ

469 views

One excellent comment on the previous entry on Epoch's error handling philosophy observed that the design really looks an awful lot like exceptions, and doesn't do much for the orthogonal separation of the four points of error handling I enumerated at the top of that post.

Let's revisit those points:


  • Propagating error information as failures occur
  • Adjusting control flow in the presence of error circumstances
  • Maintaining certain contractual semantics (this file will get closed, that memory will get freed, etc.)
  • Responding to and possibly recovering from error conditions

    In the last post, I introduced guard { } entities and the evacuate() function. Together, these address the first two points. evacuate() takes an optional parameter, which is stored in a special out-of-band register in the VM, and is recovered by the guard { } entity and (optionally) passed to a recovery function.

    An obvious question at this point arises: why tie error information with the stack unwinding mechanism of evacuate()? Consider the alternative:

    foo : () -> ()
    {
    seterror(42)
    evacuate()
    }

    entrypoint : () -> ()
    {
    guard(recovery)
    {
    foo()
    }

    recovery : (42) -> () { debugwritestring("Error in foo") }
    }


    The only real difference is that we need two function calls now: one to assign the out-of-band error information into the magic register, and one to perform stack unrolling.

    What does this really gain us? Two things: first, we can now set error information without stack unwinding; and second, we can unwind the stack without propagating error information. This seems like it really gets the orthogonality of the two points being addressed here.

    Unfortunately, the wins aren't all that great. We already had the ability to unwind the stack without propagating an error: just call evacuate() and let the nearest guard { } catch it. And if we can set error information without acting on it, all we really have done is create a special global variable, with all the evilness and treachery that that implies.

    So in my book, it makes sense to leave these two concerns tightly coupled. Error propagation and stack unwinding make sense together, in my opinion, and they'll probably remain connected in Epoch unless someone has a great argument against doing so.


    The other two points on the list, though, are where things get interesting. Let's consider recovery mechanisms first.

    Since Epoch allows nested function definitions, and also allows use-before-define code style, it is trivial to organize your error handling any way you deem appropriate. Want a global error handler? Easy! Just pass a global function to guard { }. Want a local error handler that uses a function's internal state to affect how it reacts? Also easy! This was alluded to in the previous post, but is worth looking at again in detail:

    entrypoint : () -> ()
    {
    integer(count, 0)

    guard(recovery)
    {
    while(true)
    {
    randomly_evacuate()
    ++count
    }
    }

    recovery : () -> ()
    {
    debugwritestring("Succeeded " ; cast(string, count) ; " time(s) before failing!")
    }
    }


    In essence, guard { } allows us to totally separate the code which handles errors from the code which might raise errors, in any organizational pattern we like. Since guard { } is intelligent about calling the recovery function (i.e. it supports pattern-matched parameters), we can do all manner of cool things as the situation demands. There is no obligation, as there is with exceptions in most languages, to handle errors right there in a catch() style block.


    So what about contract obligations?

    This is where things get really neat in my opinion. Consider the two principal mechanisms for upholding contractual obligations in imperative languages with exceptions: RAII (enabled by deterministic destruction), or finally() blocks (necessary in garbage collected environments).

    Epoch supports optional garbage collection. So we can't always count on RAII. We also can't always count on finally() blocks because if an object is marked for deterministic destruction, it might have been obliterated by the stack unwinding process.

    The solution is to make contracts a first-class language feature. Instead of loosely encoding the notion of "always close this file" or "always release this memory" or whatnot, we allow those concepts to be expressed directly. Moreover, for things like memory management, "release this memory" might simply be deleting a reference in garbage collected mode, or actually doing reference counting and deterministic destruction in manual mode. Think of it like having a smart pointer wrapping every memory allocation in your program, by default.

    This is a bit more hazy in terms of syntax potential, which is mostly why I didn't expand on it too much in the previous post. There are a lot of options for how to make this look, and it will take some seriously careful thought to come up with a good approach that'll survive long-term.


    I honestly haven't got a clear idea of what to make the code look like for this, but it relates heavily to the object philosophy of the language itself - something which is still heavily in flux and needs a lot more thought before I can unveil it.

    Here's a quick teaser, though:

    • Data structures do not directly allow methods to be attached to them
    • "Contracts" tie a data structure instance to a set of invariants, which are upheld by code
    • You don't invoke methods on a contract, you send it a message
    • Message passing is uniform for both contracts and parallel processing, both in syntax and implementation
    • This means you can distribute computations based on contracts without changing your code!
    • Yes, I'm stealing heavily from Erlang.

0
Sign in to follow this  
Followers 0


0 Comments


There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now