4 ms·
I think it's important to emphasize that while Haskell provides purity, it also enables the definition of impure code in a clear and distinct way, the idea bein
by mbrock 13y ago
I think it's important to emphasize that while Haskell provides purity, it also enables the definition of impure code in a clear and distinct way, the idea being to encourage a clean "separation of concerns" between logic and interaction.
It's not really that Haskell removes impurity, more that it adds purity. The "main" function of any Haskell application is an IO action, but you can define pure functions which relinquish IO and global state. And the best practices include doing that as much as possible, which is a terrific way of making your program more comprehensible and correct.
- _delirium 13y agoI wonder if it would be possible to develop an idiom for programming in a language like C that separated those concerns. Put most/all application logic into const/pure functions, and then have a separate set of interaction functions, i.e. just pull out that one bit of Haskell philosophy from the rest of the language design. Speculative guesses as to what might stand in the way: 1. The lack of a purity-tracking mechanism like Haskell's may make programming in this style in C more complex and error-prone. 2. The fact that C compilers don't expect you to use this programming style may mean they don't optimize common operations in the way Haskell does; for example, if you do manipulations on large arrays in a functional rather than imperative style, Haskell will optimize away a lot of the temporary arrays, but C compilers may not.
- tome 13y agoAbsolutely it would, but it's much easier with a type system to guide you.
- _pmf_ 13y agoGCC's pure and const function attributes pretty much allow for these optimizations.
- _delirium 13y agoFor scalar types I agree, but now that I think of it, I'm not sure it's even possible to write array code using them, unless I'm missing something. If you can't modify the array in-place or write to a provided "output" parameter, and you can't malloc a fresh array to return (because that's a side effect), how do you write a pure function of type int[] -> int[] at all? The nice thing about Haskell is that it conceptually mallocs a fresh array to return, but then optimizes away the intermediate array in a lot of cases.
- cpeterso 13y agoI believe GCC's pure and const function attributes, like the restrict keyword, are only advisory and have no extra compile-time checks that your functions are actually pure or const.
- guerrilla 13y agoI am currently trying to get some of this working in a library I call 'libalgae' in which I'm encoding algebraic data types and their eliminators (similar to how Epigram works, like an induction principle for each datatype: This allows us to rule out non-terminating programs and ensure coverage of all constructors.) I'm not very much concerned with optimization, as I'd like to use this "DSL" as a bootstrapping language for a self-hosting programming language which provides the same in a more natural way. The way I've done it so far is seems to be working, and I've implemented standard types (their introduction and elimination rules) and few functions on them. The two main problems I've run into is memory leaking and portability. Since the constructors of an algebraic data type should return const data, it's a bit difficult to free them without violating the type system. I haven't decided yet if I'll end up violating the type system or just drop that const. I think the latter makes more sense, since we're trusting C as little as possible in the first palce. Everything actually might as well be done in void pointers but I think there's some value in using types where possible for documentation's sake. About portability. The main thing here that I ran into was that there's no real standard and portable way of doing closures or anonymous functions in C yet. Right now, what I'm doing is using nested functions, which works in GCC and clang, but I have no idea if they'd work in MSVC or any of the other compilers out there like pcc and lcc. OS X has blocks, but I figure if they're using gcc too, why not stick with nested functions. As far as I can tell, I won't have the problem of having nested functions returning before their parent does because of the eliminator model. The libCello (http://libcello.org/ http://libcello.org/) author has been able to successfully model typeclasses in C, so I will be picking that up as soon as I get around to it. I pushed some of the code to GitHub just now for this post: https://github.com/guerrilla/libalgae https://github.com/guerrilla/libalgae I'm glad to hear other people had the same idea. If anyone has any suggestions, please let me know, as I wouldn't mind some collaboration on this.
- mbrock 13y agoSince beginning to see the value of purity, I write imperative code in a different way. I don't care much about local manipulation of state for algorithms and such, but I write a lot more pure functions. It's not only that purity is safe and beautiful. It's also in many cases much easier to understand, because it's about writing functions that express transformations. These transformations should make sense in a conceptual way. When you distill your domain concepts (see "Domain-Driven Design") and write pure functions on domain data, the result is remarkably clear, testable, and debuggable, no matter which language. Previously I would have been (prematurely) concerned with the efficiency of in-place updates, but since I'm not writing high-performance CPU-bound software, I don't care about that anymore until the profiler tells me to.
- vladimirralev 13y agoWhat is your imperative language? Do you write imperative code in OOP? Do you find yourself compelled to implement everything with static methods?
- mbrock 13y agoI currently work with Java. This codebase employs a kind of pseudo-OOP, according to a pattern that I think is at least somewhat popular. Data is mostly represented by objects with only getters and setters. Business logic is contained in "services," which are basically stateless singletons (handled by dependency injection). The best code in the system, IMO, consists of small stateless singletons with clear dependencies and non-modifying operations. This code often comes out of test-driven development and has tests that read like specifications.
- Fistandantilus 13y agoI believe the D language and Rust both support this kind of model, where functions can be optionally pure, and data optionally immutable. They are both C-like as well, though D seems to be closer to C than Rust.