5 ms·
While that is true, you could have accomplished the same thing through a special syntax that marked a function as "pure", and establishing the constraint that i
by lobster_johnson 13y ago
While that is true, you could have accomplished the same thing through a special syntax that marked a function as "pure", and establishing the constraint that impure functions cannot be called by pure ones. And then only allowed I/O through a set of impure base functions.
- mwotton 13y agoWhat benefit does that have over what's actually there?
- lobster_johnson 13y agoMy point was not to argue that something like that would be better solution, but since you're asking: Having a special syntax would make the learning curve a little shallower for newcomers. And it would simplify certain constructs -- instead of having to lift IO values or using mapM_ or whatever, you could actually deal with the results from impure functions directly, no unwrapping or rewrapping needed. While using the type system to implement an effects system is theoretically elegant, I think it's a beautiful hack that has made the language fussier and more obtuse in practice.
- fusiongyro 13y agoThat would certainly be true if purity enforced by monadic I/O were the end of the story, but it isn't. While new users create a lot of hot air about monads and I/O, intermediate-experienced Haskell users just use them for various different purposes and get on with life. At the end of the day most of us have differing opinions on what constitutes simplicity and elegance. It's certainly true that a "pure" annotation like you're proposing is a much smaller change to introduce in an imperative setting. I recollect D or Rust or something is doing this. But in the functional programming context the monadic solution is more general, and a two-function type class with 3 (IIRC) algebraic laws is not considered an overbearing amount of complexity, though there are of course interesting alternatives with their own merits.
- pcwalton 13y agoRust used to have a "pure" annotation, but it's gone, partially because it was a pain to have to write the annotation everywhere, partially because it's not needed for memory safety anymore, partially because nobody can agree on what "pure" means.
- fusiongyro 13y agoI wouldn't have anticipated that, but now that you say it I could see why that would lead to a debate. How would you deal with mutable data structures, for instance? What if it accesses the environment, but in a fashion you could somehow guarantee were safe? In Haskell the programmer can circumvent the system with unsafePerformIO if they know something they can't convince Haskell otherwise, but it almost seems like you'd need a "pure-but-not-really" annotation to do this kind of thing in an imperative language that actually enforced purity.
- laureny 13y agoC++ walked in these very same footsteps. First by not having const, then by having it, then by allowing exceptions to constness, then by introducing const_cast and finally by allowing temporarily mutable const objects.
- epsylon 13y agoC++ const is defective because it's a shallow const. You can modify an object through a const pointer. The D language "fixes" this by making const transitive (and also adding an immutable annotation, which means the object is truely read-only, as in "read-only memory").
- lclarkmichalek 13y ago"pure nothrow @safe" is what you get to prepend to your functions in D. I prefer Haskell's approach.
- lobster_johnson 13y agoSure. IO plays a part of a larger system of monads and functional programming, but it's not a prerequisite for impurity to exist -- it is more like a happy confluence of various strands of functional theory. After all, Haskell had I/O before the IO monad existed (although it was apparently not a happy solution). Personally, I find monadic I/O theoretically elegant, but it comes at the cost of clumsiness when applied to real-world programs. To me, Haskell's "do" blocks feel like an implicit admission of this clumsiness; they are a crutch to work around the fact that having to constantly wrap and unwrap data is something of a chore.
- tome 13y agoAnd how would you achieve that in practice?
- alipang 13y agoAs Erik Meijer said, there are many ways to be dirty, but only one way to be pure. Haskell allows you to annotate classes of side effects such as "changes state" or "might throw exception", not necessarily the full IO monad, so annotating as pure doesn't make sense.
- tmoertel 13y agoWhile (map) is pure, (map print) is not. Thus map is a higher-order function that can create both pure and impure functions depending upon the purity of its arguments. How then would your syntactic scheme allow for higher-order functions?
- mercurial 13y agomap print doesn't have side effects. mapM_ print, certainly.
- tmoertel 13y ago(mapM_ print) produces a pure function that produces a single IO action, and (map print) produces a pure function that produces a list of IO actions. Neither has any effect unless called in a context that sequences the resultant actions into the main IO action that the runtime interprets (or a thread's action).
- mercurial 13y agoI meant mapM_ in an IO action actually executed. On the other hand, getting a list of IO actions, even in the IO monad, doesn't amount to much.
- tmoertel 13y agoAnd my point is that the action that mapM_ ultimately produces is not actually executed unless you sequence it into the main action (or a thread's action). Since mapM_'s eventual action is a singleton, you can do this sequencing with any combinator taking a singleton action, for example (>>) or (>>=), but it must be sequenced nonetheless, the same as for the list of actions that (map print) produces, if you want those actions to be executed.
- mercurial 13y agoFair enough.
- hannibal5 13y agoI like the uniqueness typing approach in Clean very much. Function can manage state of variable destructively if the variable is declared unique (there can't be other references, so there can't be side effects from destructively modifying the value). It's easy to understand. It opens more avenues for fast code, and it keeps the purity.
- nimrody 13y agoHave you actually used Clean in production code? Can you share some of the tradeoffs of that approach? Looking at Clean's documentation it certainly seems nice, but I know of no other language that had adopted Uniqueness typing. [Clojure's Transients (http://clojure.org/transients http://clojure.org/transients) perhaps?]