In C# or C++ we have objects, instances of classes that are live in memory. The kernel also has objects, like interrupt objects. I wondered if these kernel objects can be thought of as we C# or C++ programmers objects?
The short answer is 'yes'. Objects are a state of mind. You can organize your work in objects in assembly language with a few macros, or in PL/I, or C, or C++. Some people might insist that it isn't an object without some sort of binding of dispatch to objects. Well, kernel/C object models use functions pointers to accomplish, somewhat more manually, what languages like C++, C#, or Java do.
What, after all, is an 'object'?
Answer 1: any lump of data that groups some related items. Any c struct. Some people would bridle and insist on ...
Answer 2: the combination of data and functions, such that code 'calls' the object, and the results depend on conditions set up by the creator of the object. So, in C++ or C# or Java, there is inheritance. Code calls SomeObject.someFunction(), and what happens depends on the inheritance graph, which is controlled by the object author, not by the caller.
In kernels, and in the pleistocene era when some of us learned to program, we accomplish(ed) the same thing, more or less, with simpler languages, using function pointers. That is to say, a slot in a structure that stores a reference to a function. The caller calls someobject.throwAnEgg, and what actually happens depends on what function pointer is sitting in `throwAnEgg'.
I think this should be tagged as subjective as the answers are going to vary and reflect the individual's personal view of things.
My take is this...
When you are talking about low-level stuff, sometimes, it is easier to bring in the perspective of OOP into it, to make it easier to communicate the concepts of what happens in the kernel level
...but...
I'd rather prefer to talk in terms of low-level nuts and bolts rather, because the complexity of the nuts and bolts can be easily solved by hammering it out, rather than talking and thinking in terms of objects because it is contriving and making a complex thing sound simple and setting yourself up for false thinking in terms of code economy.
For an example, from a kernel viewpoint, a TSS (Task State Segment) is a structure for holding the registers at the point before a task switch takes place (this is based on the processor being switched to 32bit and has paging enabled and so on). If you talk in terms of OOP aspect, i.e. a task selector object, that would not sound right because you're talking about a high level aspect when really, it is an actual nuts and bolts, take a look at the Intel 80386 programmer's manual, and there are references to the TSS, Chapter 13 - Protected-Mode Multitasking, Section 13.1 in the document 24143004.pdf available for download here
If you are talking high-level, from a high-level programming aspect, then it would be easier to define the OOP paradigm.
So, going back to your question, from a kernel aspect, you can if you wish, talk from a simplistic and concrete OOP terms, nonetheless, it would make you think in terms of having to put extra effort into coding in order to follow the OOP paradigm which may or could end up with convoluted code.
If the book you are reading is about Linux you might be looking for kobject, which is simply an abstraction supporting Linux driver model. But in general any piece of data that is somehow associated with some behaviour like set of functions, macros, etc. might be called an object. This is much relaxed from more or less formal definition of object in OO languages like C# or C++.
Related
I noticed there is a Mutex structure in Haskell. I did not understand how it works as I am not a Haskell developer, but If every variable is immutable (as FP is advocating), why do we still need a mutex?
Indeed, all variables are immutable. But they can represent, for example, references to objects and there is a class of functions that allow you to describe the process of changing the contents of these references. If a similar process is in another thread, then there will be a problem.
You can say that Haskell is a language for modeling. And it's clean. But it allows you to model not pure calculations, but actual work on impure calculations makes runtime (or by FFI). And we need to design something like a mutex into our model for multithreaded programming.
ADDITION
I think, if you really want to understand why there is something like mutex in Haskell, first of all, you should understand how Haskell can have a function, for example, like readFile which takes a file path and returns its content? Problem is that readFile must be pure and impure, which is paradoxical. So, how in Haskell this paradox solved? Try to answer on this question and I believe you will understand more things by this way.
I'm not too sure where you're finding a structure called Mutex, but if you're talking about Data.Mutex, that's for the Fay language, which is a javascript-targetting language.
If you're talking about Control.Concurrent.Lock, that's what freestyle said which is that it's modelling a locking mechanism.
The more usual way of doing inter-process concurrency, though, uses what's called Software Transactional Memory and this uses the Control.Monad.STM module which uses a form of transactional memory that has a kind of automatic locking mechanism where most of the time you don't have to worry about manual locking. This is comparatively amazing when you consider manual locking.
Rich Hickey has been responsible for pushing this idea further into the mainstream by making an implementation of it in the Clojure language, but essentially this mechanism allows the application level programmer to not worry about the extreme pain of manual locking and synchronisation mechanisms. Reads are extremely fast because of immutability and writes get replayed automatically. Simon Peyton Jones has a paper on it and here's a link to a video of him talking about it at an o'reilly conference called "Transactional Memory for Concurrent Programming": https://www.youtube.com/watch?v=4caDLTfSa2Q
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I want a better C. Let me explain:
I do a lot of programming in C, which is required for applications that have real-time needs such as audio programming, robotics, device drivers, etc.
While I love C, one thing that gets on my nerves after having spent a lot of time with Haskell is the lack of a proper type system. That is, as soon as you want to write a more general-purpose function, say something that manipulates a generic pointer, (like say a generic linked list) you have to cast things to void* or whatever, and you loose all type information. It's an all-or-nothing system, which doesn't let you write generic functions without losing all the advantages of type checking.
C++ doesn't solve this. And I don't want to use C++ anyways. I find OO classes and templates to be a headache.
Haskell and its type classes do solve this. You can have semantically useful types, and use type constraints to write functions that operate on classes of types, that don't depend on void.
But the domain I'm working in, I can't use Haskell, because it's not real-time capable--mostly due to garbage collection. GC is needed because it's very difficult to do functional programming, which is allocation-heavy, without automatic memory management. However, there is nothing specifically in the idea of type classes that goes against C's semantics. I want C, but with Haskell's dependable type system, to help me write well-typed systems. However, I really want C: I want to be in control of memory management, I want to know how the data structures are layed out, I want to use (well-typed) pointer arithmetic, I want mutability.
Is there any language like this? If so, why is it not more popular for low-level programming?
Aside: I know there are some small language experiments in this direction, but I'm interested in things that would be really usable in real-world projects. I'm interesting in growing-to-well-developed languages, but not so much "toy" languages.
I should add, I heard of Cyclone, which is interesting, but I couldn't get it to compile for me (Ubuntu) and I haven't heard of any projects actually using it.. any other suggestions in this vein are welcome.
Thanks!
Since nobody brought it up yet: I think the ATS language is a very good candidate for a better C! Especially since you enjoy Haskell and thus functional programming with strong types. Note that ATS seems to be specifically designed for systems programming and hard real-time applications as most of it can do without garbage collection.
If you check the shootout you will find that performance is basically on par with C. I think this is quite impressive since modern c compilers have years and years and years of optimization work behind them while ATS is basically developed by one guy. -- while other languages providing similar safety features usually introduce overhead ATS ensures things entirely at compile time and thus yields very similar performance characteristics as C.
To quote the website:
What is ATS?
ATS is a statically typed programming language that unifies implementation with formal specification. It is equipped with a highly expressive type system rooted in the framework Applied Type System, which gives the language its name. In particular, both dependent types and linear types are available in ATS. The current implementation of ATS (ATS/Anairiats) is written in ATS itself. It can be as efficient as C/C++ (see The Computer Language Benchmarks Game for concrete evidence) and supports a variety of programming paradigms that include:
Functional programming. The core of ATS is a functional language based on eager (aka. call-by-value) evaluation, which can also accommodate lazy (aka. call-by-need) evaluation. The availability of linear types in ATS often makes functional programs written in it run not only with surprisingly high efficiency (when compared to C) but also with surprisingly small (memory) footprint (when compared to C as well).
Imperative programming. The novel and unique approach to imperative programming in ATS is firmly rooted in the paradigm of programming with theorem-proving. The type system of ATS allows many features considered dangerous in other languages (e.g., explicit pointer arithmetic and explicit memory allocation/deallocation) to be safely supported in ATS, making ATS a viable programming language for low-level systems programming.
Concurrent programming. ATS, equipped with a multicore-safe implementation of garbage collection, can support multithreaded programming through the use of pthreads. The availability of linear types for tracking and safely manipulating resources provides an effective means to constructing reliable programs that can take advantage of multicore architectures.
Modular programming. The module system of ATS is largely infuenced by that of Modula-3, which is both simple and general as well as effective in supporting large scale programming.
In addition, ATS contains a subsystem ATS/LF that supports a form of (interactive) theorem-proving, where proofs are constructed as total functions. With this component, ATS advocates a programmer-centric approach to program verification that combines programming with theorem-proving in a syntactically intertwined manner. Furthermore, this component can serve as a logical framework for encoding deduction systems and their (meta-)properties.
What about Nimrod or Vala languages ?
Rust
Another (real) candidate for a better C is The Rust Programming Language.
Unlike some other suggestions, (Go, Nimrod, D, ...) Rust can directly compete with C and C++ because it has manual memory management and does not require garbage collection (see [1]).
What sets Rust apart is that it has safe manual memory management. (The link is to pc walton's blog, one of Rusts main contributors and generally worth a read ;) Among other things, this means it fixes the billion dollar mistake of nullpointers. Many of the other languages suggested here either require garbage collection (Go) or have garbage collection turned on by default and do not provide facilities for safe manual memory management beyond what C++ provides (Nimrod, D).
While Rust has an imperative heart, it does borrow a lot of nice things from functional languages, for example sum types aka tagged unions. It is also really concerned with being a safe and performance oriented language.
[1] Right now there are two main pointer types owned pointers (like std::unique_ptr in C++ but with better support from the typechecker) and managed pointers. As the name suggests the latter do require task-local garbage collection, but there are thoughts to remove them from the language and only provide them as a library.
EDITED to reflect #ReneSacs comment: Garbage collection is not required in D and Nimrod.
I don't know much about Haskell, but if you want a strong type system, take a look at Ada. It is heavily used in embedded systems for aerospace applications. The SIGADA moto is "In strong typing we trust." It won't be of much use, however, if you have to do Windows/Linux type device drivers.
A few reasons it is not so popular:
verbose syntax -- designed to be read, not written
compilers were historically expensive
the relationship to DOD and design committees, which programmers seem to knock
I think the truth is that most programmers don't like strong type systems.
Nim (former Nimrod) has a powerful type system, with concepts and easy generics. It also features extensive compile time mechanisms with templates and macros. It also has easy C FFI and all the low level features that you expect from a system programming language, so you can write your own kernel, for example.
Currently it compiles to C, so you can use it everywhere GCC runs, for example. If you only want to use Nim as better C, you can do it via the --os:standalone compiler switch, that gives you a bare bones standard library, with no OS ties.
For example, to compile to an AVR micro-controller you can use:
nim c --cpu:avr --os:standalone --deadCodeElim:on --genScript x.nim
Nim has a soft real-time GC where you can specify when it runs and the max pause time in microseconds. If you really can't afford the GC, you can disable it completely (--gc:none compiler switch) and use only manual memory management like C, losing most of the standard library, but still retaining the much saner and powerful type system.
Also, tagged pointers are a planned feature, that ensure you don't mix kernel level pointers with user level pointers, for example.
D might offer what you want. It has a very rich type system, but you can still control memory layout if you need to. It has unrestricted pointers like C. It’s garbage collected, but you aren’t forced to use the garbage collector and you can write your own memory management code if you really want.
However, I’m not sure to what extent you can mix the type richness with the low-level approach you want to use.
Let us know if you find something that suits your needs.
I'm not sure what state Cyclone is in, but that provided more safety for standard C. D can be also considered a "better C" to some extent, but its status is not very clear with its split-brain in standard library.
My language of choice as a "better C" is OOC. It's still young, but it's quite interesting. It gives you the OO without C++'s killer complexity. It gives you easy access to C interfaces (you can "cover" C structs and use them normally when calling external libraries / control the memory layout this way). It uses GC by default, but you can turn it off if you really don't want it (but that means you cannot use the standard library collections anymore without leaking).
The other comment mentioned Ada which I forgot about, but that reminded me: there's Oberon, which is supposed to be a safe(-er) language, but that also contains garbage collection mechanisms.
You might also want to look at BitC. It’s a serious language and not a toy, but it isn’t ready yet and probably won’t be ready in time to be of any use to you.
Nonetheless, a specific design goal of BitC is to support low-level development in conjunction with a Haskell-style type system. It was originally designed to support development of the Coyotos microkernel. I think that Coyotos was killed off, but BitC is still apparently being developed.
C++ doesn't solve this. And I don't want to use C++ anyways. I find OO classes and templates to be a headache.
Get over this attitude. Just use C++. You can start with coding C in C++ and keep gradually moving to better style.
Closed. This question needs to be more focused. It is not currently accepting answers.
Closed 6 years ago.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
What is a good way to design/structure large functional programs, especially in Haskell?
I've been through a bunch of the tutorials (Write Yourself a Scheme being my favorite, with Real World Haskell a close second) - but most of the programs are relatively small, and single-purpose. Additionally, I don't consider some of them to be particularly elegant (for example, the vast lookup tables in WYAS).
I'm now wanting to write larger programs, with more moving parts - acquiring data from a variety of different sources, cleaning it, processing it in various ways, displaying it in user interfaces, persisting it, communicating over networks, etc. How could one best structure such code to be legible, maintainable, and adaptable to changing requirements?
There is quite a large literature addressing these questions for large object-oriented imperative programs. Ideas like MVC, design patterns, etc. are decent prescriptions for realizing broad goals like separation of concerns and reusability in an OO style. Additionally, newer imperative languages lend themselves to a 'design as you grow' style of refactoring to which, in my novice opinion, Haskell appears less well-suited.
Is there an equivalent literature for Haskell? How is the zoo of exotic control structures available in functional programming (monads, arrows, applicative, etc.) best employed for this purpose? What best practices could you recommend?
Thanks!
EDIT (this is a follow-up to Don Stewart's answer):
#dons mentioned: "Monads capture key architectural designs in types."
I guess my question is: how should one think about key architectural designs in a pure functional language?
Consider the example of several data streams, and several processing steps. I can write modular parsers for the data streams to a set of data structures, and I can implement each processing step as a pure function. The processing steps required for one piece of data will depend on its value and others'. Some of the steps should be followed by side-effects like GUI updates or database queries.
What's the 'Right' way to tie the data and the parsing steps in a nice way? One could write a big function which does the right thing for the various data types. Or one could use a monad to keep track of what's been processed so far and have each processing step get whatever it needs next from the monad state. Or one could write largely separate programs and send messages around (I don't much like this option).
The slides he linked have a Things we Need bullet: "Idioms for mapping design onto
types/functions/classes/monads". What are the idioms? :)
I talk a bit about this in Engineering Large Projects in Haskell and in the Design and Implementation of XMonad. Engineering in the large is about managing complexity. The primary code structuring mechanisms in Haskell for managing complexity are:
The type system
Use the type system to enforce abstractions, simplifying interactions.
Enforce key invariants via types
(e.g. that certain values cannot escape some scope)
That certain code does no IO, does not touch the disk
Enforce safety: checked exceptions (Maybe/Either), avoid mixing concepts (Word, Int, Address)
Good data structures (like zippers) can make some classes of testing needless, as they rule out e.g. out of bounds errors statically.
The profiler
Provide objective evidence of your program's heap and time profiles.
Heap profiling, in particular, is the best way to ensure no unnecessary memory use.
Purity
Reduce complexity dramatically by removing state. Purely functional code scales, because it is compositional. All you need is the type to determine how to use some code -- it won't mysteriously break when you change some other part of the program.
Use lots of "model/view/controller" style programming: parse external data as soon as possible into purely functional data structures, operate on those structures, then once all work is done, render/flush/serialize out. Keeps most of your code pure
Testing
QuickCheck + Haskell Code Coverage, to ensure you are testing the things you can't check with types.
GHC + RTS is great for seeing if you're spending too much time doing GC.
QuickCheck can also help you identify clean, orthogonal APIs for your modules. If the properties of your code are difficult to state, they're probably too complex. Keep refactoring until you have a clean set of properties that can test your code, that compose well. Then the code is probably well designed too.
Monads for Structuring
Monads capture key architectural designs in types (this code accesses hardware, this code is a single-user session, etc.)
E.g. the X monad in xmonad, captures precisely the design for what state is visible to what components of the system.
Type classes and existential types
Use type classes to provide abstraction: hide implementations behind polymorphic interfaces.
Concurrency and parallelism
Sneak par into your program to beat the competition with easy, composable parallelism.
Refactor
You can refactor in Haskell a lot. The types ensure your large scale changes will be safe, if you're using types wisely. This will help your codebase scale. Make sure that your refactorings will cause type errors until complete.
Use the FFI wisely
The FFI makes it easier to play with foreign code, but that foreign code can be dangerous.
Be very careful in assumptions about the shape of data returned.
Meta programming
A bit of Template Haskell or generics can remove boilerplate.
Packaging and distribution
Use Cabal. Don't roll your own build system. (EDIT: Actually you probably want to use Stack now for getting started.).
Use Haddock for good API docs
Tools like graphmod can show your module structures.
Rely on the Haskell Platform versions of libraries and tools, if at all possible. It is a stable base. (EDIT: Again, these days you likely want to use Stack for getting a stable base up and running.)
Warnings
Use -Wall to keep your code clean of smells. You might also look at Agda, Isabelle or Catch for more assurance. For lint-like checking, see the great hlint, which will suggest improvements.
With all these tools you can keep a handle on complexity, removing as many interactions between components as possible. Ideally, you have a very large base of pure code, which is really easy to maintain, since it is compositional. That's not always possible, but it is worth aiming for.
In general: decompose the logical units of your system into the smallest referentially transparent components possible, then implement them in modules. Global or local environments for sets of components (or inside components) might be mapped to monads. Use algebraic data types to describe core data structures. Share those definitions widely.
Don gave you most of the details above, but here's my two cents from doing really nitty-gritty stateful programs like system daemons in Haskell.
In the end, you live in a monad transformer stack. At the bottom is IO. Above that, every major module (in the abstract sense, not the module-in-a-file sense) maps its necessary state into a layer in that stack. So if you have your database connection code hidden in a module, you write it all to be over a type MonadReader Connection m => ... -> m ... and then your database functions can always get their connection without functions from other modules having to be aware of its existence. You might end up with one layer carrying your database connection, another your configuration, a third your various semaphores and mvars for the resolution of parallelism and synchronization, another your log file handles, etc.
Figure out your error handling first. The greatest weakness at the moment for Haskell in larger systems is the plethora of error handling methods, including lousy ones like Maybe (which is wrong because you can't return any information on what went wrong; always use Either instead of Maybe unless you really just mean missing values). Figure out how you're going to do it first, and set up adapters from the various error handling mechanisms your libraries and other code uses into your final one. This will save you a world of grief later.
Addendum (extracted from comments; thanks to Lii & liminalisht) —
more discussion about different ways to slice a large program into monads in a stack:
Ben Kolera gives a great practical intro to this topic, and Brian Hurt discusses solutions to the problem of lifting monadic actions into your custom monad. George Wilson shows how to use mtl to write code that works with any monad that implements the required typeclasses, rather than your custom monad kind. Carlo Hamalainen has written some short, useful notes summarizing George's talk.
Designing large programs in Haskell is not that different from doing it in other languages.
Programming in the large is about breaking your problem into manageable pieces, and how to fit those together; the implementation language is less important.
That said, in a large design it's nice to try and leverage the type system to make sure you can only fit your pieces together in a way that is correct. This might involve newtype or phantom types to make things that appear to have the same type be different.
When it comes to refactoring the code as you go along, purity is a great boon, so try to keep as much of the code as possible pure. Pure code is easy to refactor, because it has no hidden interaction with other parts of your program.
I did learn structured functional programming the first time with this book.
It may not be exactly what you are looking for, but for beginners in functional programming, this may be one of the best first steps to learn to structure functional programs - independant of the scale. On all abstraction levels, the design should always have clearly arranged structures.
The Craft of Functional Programming
http://www.cs.kent.ac.uk/people/staff/sjt/craft2e/
I'm currently writing a book with the title "Functional Design and Architecture". It provides you with a complete set of techniques how to build a big application using pure functional approach. It describes many functional patterns and ideas while building an SCADA-like application 'Andromeda' for controlling spaceships from scratch. My primary language is Haskell. The book covers:
Approaches to architecture modelling using diagrams;
Requirements analysis;
Embedded DSL domain modelling;
External DSL design and implementation;
Monads as subsystems with effects;
Free monads as functional interfaces;
Arrowised eDSLs;
Inversion of Control using Free monadic eDSLs;
Software Transactional Memory;
Lenses;
State, Reader, Writer, RWS, ST monads;
Impure state: IORef, MVar, STM;
Multithreading and concurrent domain modelling;
GUI;
Applicability of mainstream techniques and approaches such as UML, SOLID, GRASP;
Interaction with impure subsystems.
You may get familiar with the code for the book here, and the 'Andromeda' project code.
I expect to finish this book at the end of 2017. Until that happens, you may read my article "Design and Architecture in Functional Programming" (Rus) here.
UPDATE
I shared my book online (first 5 chapters). See post on Reddit
Gabriel's blog post Scalable program architectures might be worth a mention.
Haskell design patterns differ from mainstream design patterns in one
important way:
Conventional architecture: Combine a several components together of
type A to generate a "network" or "topology" of type B
Haskell architecture: Combine several components together of type A to
generate a new component of the same type A, indistinguishable in
character from its substituent parts
It often strikes me that an apparently elegant architecture often tends to fall out of libraries that exhibit this nice sense of homogeneity, in a bottom-up sort of way. In Haskell this is especially apparent - patterns that would traditionally be considered "top-down architecture" tend to be captured in libraries like mvc, Netwire and Cloud Haskell. That is to say, I hope this answer will not be interpreted as an attempt replace any of the others in this thread, just that structural choices can and should ideally be abstracted away in libraries by domain experts. The real difficulty in building large systems, in my opinion, is evaluating these libraries on their architectural "goodness" versus all of your pragmatic concerns.
As liminalisht mentions in the comments, The category design pattern is another post by Gabriel on the topic, in a similar vein.
I have found the paper "Teaching Software Architecture Using Haskell" (pdf) by Alejandro Serrano useful for thinking about large-scale structure in Haskell.
Perhaps you have to go an step back and think of how to translate the description of the problem to a design in the first place. Since Haskell is so high level, it can capture the description of the problem in the form of data structures , the actions as procedures and the pure transformation as functions. Then you have a design. The development start when you compile this code and find concrete errors about missing fields, missing instances and missing monadic transformers in your code, because for example you perform a database Access from a library that need a certain state monad within an IO procedure. And voila, there is the program. The compiler feed your mental sketches and gives coherence to the design and the development.
In such a way you benefit from the help of Haskell since the beginning, and the coding is natural. I would not care to do something "functional" or "pure" or enough general if what you have in mind is a concrete ordinary problem. I think that over-engineering is the most dangerous thing in IT. Things are different when the problem is to create a library that abstract a set of related problems.
I hear a lot about functional languages, and how they scale well because there is no state around a function; and therefore that function can be massively parallelized.
However, this makes little sense to me because almost all real-world practical programs need/have state to take care of. I also find it interesting that most major scaling libraries, i.e. MapReduce, are typically written in imperative languages like C or C++.
I'd like to hear from the functional camp where this hype I'm hearing is coming from..
It's important to add one word: "there's no shared state".
Any meaningful program (in any language) changes the state of the world. But (some) functional languages make it impossible to access the same resource from multiple threads simultaneously. The absence of shared state makes multithreading safe.
Functional languages such as Haskell, Scheme and others have what are called "pure functions". A pure function is a function with no side effects. It doesn't modify any other state in the program. This is by definition threadsafe.
Of course you can write pure functions in imperative languages. You also find multi-paradigm languages like Python, Ruby and even C# where you can do imperative programming, functional programming or both.
But the point of Haskell (etc) is that you can't write a non-pure function. Well that's not strictly true but it's mostly true.
Similarly, many imperative languages have immutable objects for much the same reason. An immutable object is one whose state doesn't change once created. Again by definition an immutable object is threadsafe.
You're talking about two different things and don't realize it.
Yes, most real-world programs have state somewhere, but if you want to do multithreading, that state should not be everywhere, and in fact, the fewer places it's in, the better. In functional programs, the default is not to have state, and you can introduce state exactly where you need it and nowhere else. Those parts that are dealing with state will not be as easily multithreaded, but since all the rest of your program is free of side-effects and thus it doesn't matter what order those parts are executed in, it removes a huge barrier to parallelization.
However, this makes little sense to me because almost all real-world
practical programs need/have state to take care of.
You'd be surprised! Yes, all programs need some state (I/O in particular) but often you don't need much more. Just because most programs have heaps of state doesn't mean they need it.
Programming in a functional language encourages you to use less state, and thus your programs become easier to parallelise.
Many functional languages are "impure" which means they allow some state. Haskell doesn't, but Haskell has monads which basically let you get something from nothing: you get state using stateless constructs. Monads are a bit fiddly to work with which is why Haskell gives you a strong incentive to restrict state to as small a part of your program as possible.
I also find it interesting that most major scaling libraries, i.e.
MapReduce, are typically written in imperative languages like C or C++.
Programming concurrent applications is "hard" in C/C++. That's why it's best to do all the dangerous stuff in a library which is heavily tested and inspected. But you still get the flexibility and performance of C/C++.
Higher order functions. Consider a simple reduction operation, summing the elements of an array. In an imperative language, programmers typically write themselves a loop and perform reductions one element at a time.
But that code isn't easy to make multi-threaded. When you write a loop you're assuming an order of operations and you have to spell out how to get from one element to the next. You'd really like to just say "sum the array" and have the compiler, or runtime, or whatever, make the decision about how to work through the array, dividing up the task as necessary between multiple cores, and combining those results together. So instead of writing a loop, with some addition code embedded inside it, an alternative is to pass something representing "addition" into a function that can do the divvying. As soon as you do that, you're writing functionally. You're passing a function (addition) into another function (the reducer). If you write this way then it not only makes more readable code, but when you change architecture, or want to write for heterogeneous architecture, you don't have to change the summer, just the reducer. In practice you might have many different algorithms that all share one reducer so this is a big payoff.
This is just a simple example. You may want to build on this. Functions to apply other functions on 2D arrays, functions to apply functions to tree structures, functions to combine functions to apply functions (eg. if you have a hierarchical structure with trees above and arrays below) and so on.
Me and my friend are in the first stages of creating a domain-specific language designed for game programming, for his thesis paper.
The language will be fairly low-level, it will have a C-like syntax, optional garbage collection, and will be geared towards systems that have little memory or processing power (ie. Nintendo DS), but should be powerful enough to facilitate PC development easily.
It won't be a scripting language, but a compiled one, but as we don't want to spend months writing a normal compiler, the first implementation will basically be a LanguageName-to-C translator, with TCC or GCC as the end compiler.
Now, I have a question for all you game programmers out there:
What would you like to see in such a language? What features, implementation- and syntax-wise, would be best for it? What to avoid?
Edit:
Some things we already thought up:
state-based objects - an object can exist in one of it's states (or sub-states)
events and functions - events don't have to exist to be called, and can bubble up
limited dynamic allocation and pointer support - we want it to be as safe as possible
support for object compositing (Hero is composed (dynamically) of Actor, Hurtable, Steerable, etc.)
"resources" in states, loaded and unloaded automatically at beginning/end of state (for example, an OpenGL texture object is a resource)
basic support for localization and serialization
a syntax that is quickly parsable
we want to make the language as consistent as possible: everything is passed as value, every declaration has predictable syntax (eg. function retType name(type arg) is (qualifier, list) { }; no const, static, public qualifiers anywhere except in the qualifier list), etc.
Something to make concurrent programming easier. A blend of Erlang and C++ perhaps. I've been thinking about this on and off ever since the Cell processor was announced but it would take a serious chunk of R&D time to develop it and solve many of the problems that already have solutions in traditional C++ programs.
Personally I enjoy writing games that are able to access the wide, wide audience of the web. Would be beyond interesting to make it simple to interface between the desktop and the web.
That is probably more the domain of apps built with the language than the language itself, I suppose, but perhaps something that's useful to keep in mind during the design phase.
You could do worse than to read this: The Next Mainstream Programming Languages: A Game Developer's Perspective (PDF).
So, I don't want to really bust your bubble, but... maybe I should? As a professional game developer, I have to say that there really need to be three types of "languages" for game development.
First, there's your engine-level language. This is typically C++. It's all about performance. The artifacts of gameplay are not meant to be implemented here (sadly, they often are).
Next comes the gameplay language. This is lightweight, easy to understand, and designed for rapid iteration.
Finally, there is some sort of visual scripting language. This is the lightest of all and is geared toward non-programmers (level designers, etc.).
That being said:
Definitely check out UnrealScript. It's used throughout the industry (since the Unreal Engine is a cornerstone of FPS game development).
I would highly recommend supporting:
Concurrent programming (check out what CCP does with Stackless Python for Eve Online)
Network replication (check out UnrealScript, you can tag functions to run on either the server or the client, or to be safe to run on the client, etc.)
State (as mentioned) would be great. UnrealScript has this facility. This needs to be safely done (i.e., enter and exit at any point, complex transitions handled elegantly, etc.)
Good luck!