Re: [Algorithms] Complexity of new hardware
Brought to you by:
vexxed72
|
From: Conor S. <bor...@ya...> - 2009-04-26 19:11:55
|
"Could you expand on this? Some specifics perhaps? I'm not sure I understand what you mean. At least not when you compare to C++ which doesn't even have a module system (though Haskell's module system is fairly spartan)!"
Somewhere between type-classes and modules sits the "component" that Haskell has not yet mastered. C++ is reasonable for communicating "component frameworks" and Haskell is not quite there yet. The attempt at object orientation in C++ at least gives you to organize your "larger" thoughts in a way other people can understand (this is an entity, it does these things and it interacts with those things) and I don't think that Haskell has mastered this level of organisation yet. It's a good language for elegantly expressing algorithms and transforms, but it's not yet a full software engineering tool, with an easy methodology for relating with real world problems.
""Sequences of "stuff" does not imply imperative languages. The low level rendering abstraction could easily be a list of "Commands" (we could call them "command buffers", or maybe "display lists", hang on a minute!), rather than being a series of state modifying statements. In fact, a lot of abstractions treat graphics as a tree, hiding the details of the underlying state machine. At a higher level I do agree with Sweeney that graphics is pretty functional. Take something like a pixel shader, for instance, which is just a pure function really, even though most shader languages make it look like an imperative function inside (to look like C, usually).
Furthermore, if we're talking about Haskell specifically I'd say that in many ways it has much better support for imperative programming than C++ does, since you can define your own imperative sub-languages (e.g. you could have your CommandBuffer monad and write state setting etc. if that's how you really want to think about graphics). C++ doesn't allow you to abstract over what *kind* of statements you're working with, it only has one "kitchen sink" kind, and no way of overloading the way a block of multiple statements are bound together."
But sequenced execution and state setting are the native mode of imperative languages. Pure functional lazy languages, like Haskell, require abstractions to deal with that, which are an extra level of thought (and complexity). In C++, you can have a CommandBuffer without the monad (or having to think about having a monad, which is the important part). Sure, there are very functional aspects to rendering (shading is a good example) and if you want to think of it in a functional way you can, but in games currently you don't necessarily want to be that abstracted from the process. So I agree that graphics are fuctional, but only up to a point. If you're doing hardware interataction for rasterization (which most of us currently are), then graphics are a wild mix of imperative and functional (running on multiple pieces of hardware and multiple 3rd party pieces of software) and can not be categorized into one or the other area completely. Hence, taking an
absolute approach in either direction is probably not going to get you the best system.
"The problem with only going half way is that certain properties really need to be absolute unless they are to disappear. You can't be "sort of " pregnant, you either are or you aren't."
Yes, but I can't remember the last time I programmed a simulation of being pregnant (I'm not saying I haven't...). We shouldn't trap ourselves into a false dichotomy here. Software projects are incredibly complicated, not simple yes or no (are or aren't) questions. They may be a huge number of yes or no questions combining to make a nice fuzzy "yes/no" stew that is quite difficult to think of as a whole, but to be trapped in a single mindset is to miss the point of a software development.
"The key killer feature of Haskell for me is purity, and if you start allowing ad-hoc undisciplined use of side effects anywhere then the language is no longer pure. Either you contain side effects by design and enforce it, or you can never write code that relies on something being pure (parallelism!) without basically giving up on the language helping you, relying instead on convention (which always breaks)."
This is part of why I contend that Haskell is a good language in the small. That kind of purity is great at a micro level, but sometimes it is nice to do things another way. In fact, part of the beauty of pure functional programming is that you can apply it absolutely over a small area and then use that to compose code in a larger impure language, as long as the contract of the pure bits are enforced when you use them.
"Note that purity does emphatically *not* mean "no state mutations ever", it merely means that IO has to happen at the "bottom" of the program, not deep inside application code (this is pretty much what we do already though, so not much of an issue IMO), and that any localized side effects have to be marked up so that the compiler can enforce that they don't "leak" (e.g. you may want to do some in-place operations on an array for performance in a function, but from the outside the function still looks pure - that's fine in FP, Haskell uses the ST monad for it - you just need to seal off these local "bubbles" of impurity by explicitly marking up where things start going impure). I agree with Sweeney, again, that "side effects anywhere" is not the right default in a parallel world.
So really it's all about being disciplined about it and marking up functions that are impure up front, so that the compiler can be sure that you're not trying to do anything impure in a context where purity is required (e.g. parallelism, or lazy evaluation)."
I agree that side effects anywhere is not the right ideal (and Haskell does really put them into a nice little monad focused box), but I tend to lean towards a less absolute way of going about that. When you're doing a complex and heavy IO oriented operation, it sometimes makes sense to be able to do that in the simple way an imperative language allows (they're very good at it!) without the extra level of abstraction. If you want to, in most imperative languages today, you can even isolate it behind an interface such that it can not be misused in an unsafe/leaky way (one of the selling points of object orientation, I believe).
What I tend to think the right thing is to move to is a system where purity is an interface annotation that is then enforced in the compilation. Of course, I tend to think that data structure/type information should live in a language independent schema that is available at compile time to the "code", but I'm kind of crazy like that.
Cheers,
Conor
The new Internet Explorer 8 optimised for Yahoo!7: Faster, Safer, Easier. |