14 ms·
Why the world needs Haskell
- codemac 13y agoBecause how else would you implement Agda? Hah! The idea of managing side effects as a type (monads) still hasn't seemed compelling to me in terms of development time. Here I agree with Liskov in saying that it's a bit over the top[0]. Most of the quality I see with Haskell has to do with strict types, not the handling of I/O errors as types themselves. Not that I want to bash it.. Learning haskell has actively changed how I approach all my C/C++ development, and gotten me far far far into the weeds of now learning Agda as a prototyping language for designs/semantics[1]. The world may or may not need Haskell. However it's certainly a better place now that it has it. [0]: She said this at a talk she gave at work.. It was similar or the same as her "The Power of Abstraction" talk, dunno if she makes the same comment in every presentation though. [1]: http://www.youtube.com/watch?v=vy5C-mlUQ1w http://www.youtube.com/watch?v=vy5C-mlUQ1w
- deleted 13y ago[deleted]
- tmoertel 13y agoThe big win of "managing side effects as a type" is not that effectful code gets an IO type but that everything else does not. Thus the lack of IO in a function's type assures you, reliably, that the function does not cause side effects or depend upon the state of the outside universe. This lets you corral effectful code and keep the bulk of your code "pure" and easy to reuse and reason about.
- spullara 13y agoI would love to use Haskell for the algorithmic part of my code and some other language for the rest of it. I think that is what he is trying to get across. Just get rid of the IO monad and allow only call calls to Haskell from this other language — similar separation but without all the weirdness (IMHO).
- Tuna-Fish 13y agoUmm, doesn't Haskell sort of provide this with do notation? This is how programming simple things in Haskell feels like to me -- I build a lot of simple pure functions, and then bring them together in do blocks, which feel like a completely different, imperative language.
- spullara 13y agoThe fact that you have to use the IO monad, to me, feels like something completely ugly and different from what I get from haskell algorithm wise. IMHO.
- chongli 13y agoI happen to find the IO monad incredibly beautiful and elegant. Haskell lets me define my own control structures for combining IO actions in a far more powerful and elegant manner than other languages.
- lobster_johnson 13y agoWhile 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.
- 13y ago
- laureny 13y ago> Thus the lack of IO in a function's type assures you, reliably, that the function does not cause side effects No. IO is not the only monad encoding side effects.
- tmoertel 13y agoIndeed. To be precise, I should have written that the lack of X in a function's type assures you that the function does not cause X effects.
- dschiptsov 13y agoHow it so significantly better than using, say, Erlang or CL with a strict self-discipline (separation of impure functions, using appropriate naming conventions, etc.?
- implicit 13y agoDiscipline is great, but it's finite and has to come not just from you, but from everyone else on your team as well. The compiler remains vigilant and uncompromising forever. It's best to automate everything you can, and ask your team for discipline only as a last resort.
- dschiptsov 13y agoOK, let's say that it is better first to learn how to make trees with conses and traverse them using maps and folds with null? as a base case, and then enjoy a compiler which won't allow you to "cons" improper element to it.)
- nightski 13y agoWhy is this better? I see no obvious reason. Just Cons Nothing values.
- seanmcdirmid 13y agoTmoertel, your comment is dead for some reason and you might want to talk to the admins.
- ghswa 13y agoI don't agree that managing effects is over the top per se however the use of monads feels over the top for pretty much everything! It's always struck me as strange that value (as in a typical type system) and effects would be controlled through the same system. The type signature of a function and it's effects seem very much orthogonal to me. If we want to be controlling side effects then we really ought to be using a separate effect system[1]. There's a scala plugin demonstrating this (although I've not tried it)[2]. With separate type and effect systems I should be able to define a pure function fib(n) and call it like this fib(getValueFromUser()) that is without having to use special operators to get at the value which can only be used in certain contexts a la Haskell. [1] http://en.wikipedia.org/wiki/Effect_system http://en.wikipedia.org/wiki/Effect_system [2] https://github.com/lrytz/efftp/wiki https://github.com/lrytz/efftp/wiki
- gohrt 13y agox = fib(getValueFromUser()) x is not pure, but fib is. OK, the compiler can figure that out without requiring the programmer to write a special "bind" operator. How about this: dofib(argumentProvider) = fib(argumentProvider()) dofib(lambda: 1) // pure difib(getValueFromUser) // effectful Is dofib pure or not? That depends on the value of argumentProvider cannot be determined statically.
- ghswa 13y agoUsing the mechanic employed by the scala plugin I linked to[1], dofib would be annotated with pure(argumentProvider). dofib itself is pure however, at each call site, it has the effect of its argument for that call. This is consistent with your example. [1] https://github.com/lrytz/efftp/wiki/Relative-Effects https://github.com/lrytz/efftp/wiki/Relative-Effects edit: That's effectively (sorry!) higher-order effects, behaving just as you'd expect.
- Silhouette 13y agoIs dofib pure or not? Food for thought: 1. The easy but limited solution is that this code doesn't compile, because argumentProvider must have a single type/effect and you couldn't have both a pure and an impure function with that type/effect. 2. Is purity that important? Ultimately we care about avoiding our programs doing unintended things, and often effects are just fine as long as they don't misbehave in some way. Purity is a means to an end. 3. It's fascinating to extend the ideas of generic programming from mainstream type systems to effect systems. I suspect there is a lot of potential benefit to be had if we can figure out how to do this without introducing a lot of boilerplate code, in the same way that we can write code using generic types to various degrees today but have type inference spare us a lot of keyboard bashing.
- nilkn 13y agoMonads don't really have much to do with IO. It's up in the air whether IO really even is a monad. See Conal Elliot's answer on SO and the link he provides: http://stackoverflow.com/a/16444789/65799 http://stackoverflow.com/a/16444789/65799
- marshray 13y agoYou mean...all this time... ! Reminds me of the story of the old monk who emerges from the basement of the monestary holding an ancient parchment. Tears are streaming down his face. The student asks "What's wrong?" The old monk replies "All this time! We were supposed to be celebRate!"
- duaneb 13y agoI just assumed IO meant mutation.
- Zak 13y agoA thing that people are often unaware of when trying to understand how the IO type works in Haskell is that you cannot define the IO type in Haskell.
- kenko 13y agoSure you can: http://hackage.haskell.org/packages/archive/ghc-prim/0.2.0.0/doc/html/src/GHC-Types.html#IO http://hackage.haskell.org/packages/archive/ghc-prim/0.2.0.0... You can't define RealWorld, though, nor (IIRC) is IO really a state monad.
- Zak 13y agoI used "define" imprecisely. You cannot, so far as I know, implement `>>=` for the IO type defined in the above link within Haskell, which means you can't actually use it to do IO.
- kenko 13y agohttp://www.haskell.org/ghc/docs/latest/html/libraries/base/src/GHC-Base.html#returnIO http://www.haskell.org/ghc/docs/latest/html/libraries/base/s...
- implicit 13y agoI'm having fantastic success with a custom IO-like monad that lets me statically verify that my unit test suite is totally side effect free. I don't have to resort to documentation or code reviews to ensure that my teammates write fast, reliable tests. This isn't possible in a language that doesn't restrict side effects.
- pspeter3 13y agoI think this can be generalized to functional languages in general. Scala provides a lot of the same features in here.
- tieTYT 13y agoCan you list those same features that it provides? My understanding of Scala's philosophy is it provides as many features as it can. A Maybe type, immutable state, separation of IO, etc. isn't very useful if my coworker can choose not to use it.
- ionforce 13y agoScala is like Perl in that there is a non-zero possibility of writing non-native code. In Scala, you're supposed to favor immutability and functional constructs, but there's nothing preventing you from treating Scala exactly like Java. Same with Perl and C. Also, if you need a language to dictate your coworker's behavior, that says something more about your coworker than the language. Why doesn't he just choose to write good code? (barring arguments regarding the activation energy of good versus bad code)
- ericssmith 13y agoI assume by "non-native", you mean "non-idiomatic"? Since I recently went through coworkers writing "bad" (by which I mean non-idiomatic) Scala, and repairing the situation, I can say that it was surprisingly easy to lay down "functional constructs" and the result was amazing. The code became reliable, well-structured, and easy to leverage. Switching to Scala for our projects -- including introducing procedurally minded programmers to it -- was a huge win for us.
- pspeter3 13y agoYeah, we discovered that for my team's projects as well. Having a scala "guru" define and teach idiomatic scala has actually increased our productivity a lot. Any non-idiomatic scala is now purely for interacting with our existing Java libraries
- tieTYT 13y agoI don't have much experience with haskell beyond Learn You A Haskell (a book), but I wonder how beneficial the no null pointer errors are in practice. The way haskell handles this reminds me of checked exceptions in Java. In java if you read from a file, your code won't compile unless you have a catch block that handles the possibility of an IO exception. This is called a checked exception because you have to check for the possibility or else your code won't compile. I know many Java programmers handle checked exceptions by wrapping the checked exception in a unchecked exception so they don't have to deal with it. Don't Haskell developers end up doing the same thing with their Maybe concept? Haskell avoids null by using a Maybe type/class (I always forget the terminology). A Maybe can evaluate to either a value or a type that represents the absence of a value. (This is an oversimplification for the consideration of people who know nothing about Haskell) For example, you've got an associative map data type and you find an element in there. At the time of writing the code you "knew" that the element "has to be there". Haskell makes you deal with the possibility that it's not. Won't most developers just end up throwing an exception in that case so they don't have to deal with the impossible possibility? Then, x months from now when the code gets changed so that the map won't have the element there, all the sudden your code gets an error. How is this different from a null pointer exception in any other language? (Part of me is ignorant and part of me is playing the devil's advocate.)
- nightski 13y agoTwo large differences - a.) Haskell gives you tools to deal with it. Unlike checked exceptions which must be restated and dealt with in every function along the chain - Haskell provides powerful abstraction capabilities to manage this. (You can use maybe values while most of the functions know nothing about them) b.) This is a tool that can help you if you don't shoot yourself in the foot. The compiler can provide guarantees that no one "just throws an exception" by throwing warnings/errors on partial functions and or the use of unsafePerformIO. These are the only two escape hatches that would allow you to do such a thing but they are both heavily frowned upon and detectable by the compiler.
- tikhonj 13y agoThis really depends. The usual rule of thumb is that a runtime exception (at least in pure code) is for bugs in the code and explicit methods like Maybe are for bugs in the input. So if getting a Nothing from your map means your own code is broken, an exception is perfectly fine; if it means your code was called wrong, you want to just return a Maybe. In practice, most of the time, you end up either coming up with a default or returning the Maybe. This is greatly helped by the fact that just propagating Maybe value is really easy because Maybe is a member of a bunch of standard typeclasses like Applicative, Alternative and Monad. Thanks to this, I've found most of my code follows a simple pattern: it pushes the maybe values through until it has a meaningful default. This is safe, simple and fairly elegant. At some point, I either pattern match against it or use a function like fromMaybe, which then allows me to plug everything back into normal code.
- vinkelhake 13y agoboost::optional<User> get_user_by_name(string); boost::variant<string, User> get_user_by_name(string); Optional is making its way into the standard library.
- chongli 13y agoRight, but how does that give me any assurance that the value contained within the optional is itself not null? Haskell gives me that assurance. If all pointers can be null, then every pointer type is an implied optional type. Haskell's advantage is that it allows us to define types which cannot be null.
- vinkelhake 13y agoAn optional<T> has two states: it either holds a T or nothing. There's no third state. All pointers can be null, but unlike (for example) Java, we don't have to deal with pointers to objects all the time. C++ has value types and it's perfectly possible to pass objects that cannot be null.
- mercurial 13y agoYou have an option type in Guava as well, but compared to Haskell, it's annoying to use due to the lack of pattern matching. Also, you can't magically chain them the way you can do with Maybe.
- chongli 13y agoAn optional<T> has two states: it either holds a T or nothing. But T could be a pointer to a valid object or a null pointer, or am I wrong? C++ has value types and it's perfectly possible to pass objects that cannot be null. Only if those values live on the stack. What about heap objects?
- ajuc 13y agoIt's possible, no matter where the object lives: #include <cstdio> using namespace std; class Type { public: Type(int a):_a(a){}; int _a; }; Type& getType(int a) { Type* t = NULL; t = new Type(a); return (*t); } Type& doSthWith(Type& t) { //no need (and no way) to check if t is reference to proper object t._a=t._a+1; return t; } int main() { Type& t = doSthWith(getType(5)); printf("%d\n",t._a); return 0; }; This can be broken of course, for example by Type& getType(int a) { Type* t = NULL; return (*t); } But that's another matter.
- re_todd 13y agoReading Ruby code makes me happy, reading Haskell code makes me want to pull all my hair out. Enough said.
- scardine 13y agoI would say that about Python, and probably anyone can say the same about his own pet computer language. I like some of the Haskell concepts, and it seems to attract very smart folks that are heavy on math and CS jargon. This reflects on the materials and channels around the Haskell community and IMHO makes everything more intimidating than it has to be.
- lobster_johnson 13y agoHave you written any Haskell? As a Ruby dev, I found your statement to be true until I actually tried to write a program (an actual one, not a toy). Fluency came only when I had to write code.
- reitzensteinm 13y ago"I love playing the tumpet, but I grabbed a violin and it sounded like a cat dying. Enough said."
- platz 13y agoHaskell is a puzzle language (http://prog21.dadgum.com/38.html http://prog21.dadgum.com/38.html). I intend to learn some Haskell but I keep wondering if it might be more useful to spend some time with Clojure or F#
- spullara 13y agoPeople who program because they like puzzles, puzzle me. I like things that are straight forward and obvious when I program.
- platz 13y agoSure, but if you're mr. straight forward and obvious all the time, I bet your skills will plateau faster than the puzzler.
- merijnv 13y ago> I like things that are straight forward and obvious when I program. So do I, which is why I program in Haskell rather than the other languages which make me worry about a ton of irrelevant things like the global state of my program and don't let me enforce invariants of my code in the type system, guaranteeing I can't violate without making the type checker complain.
- cgag 13y agoStraight forward and obvious meaning directly align with the first thing you learned when learning to program. I like my languages straight forward, that's why I stick to English.
- tome 13y agoPersonally I find Python, Ruby, Lua, C to be "puzzle" languages. The puzzle is how to manipulate the primatives like arrays, pointers, dictionaries, etc. to express the natural, higher-level concepts that you really want, like map, fold, etc. Programmers in my previous job would feel pleased with themselves if they'd solved the "puzzle" of an array processing loop which was a simple recursive function in disguise.
- 13y ago
- _pmf_ 13y ago> It’s also why ruthless testing and 100% test coverage have become so important in mainstream languages. But even with 100% test coverage you can’t be sure that your code will work correctly if a function unexpectedly returns nil unless you’re also mocking things out or using fuzz testing. You can write tests with 100% coverage that don't use a single assertion. Test coverage is a completely useless metric, but it is an easy one to measure and understand, which is why it is so popular in pseudo-QA and the management tier.
- ghswa 13y agoI frequently see people talking about an unqualified "100% test coverage". Is it safe to assume in these cases that the author is referring to statement coverage[1] rather than something more stringent like decision coverage or even MC/DC?[2] If we're talking about statement coverage then I agree that achieving 100% statement coverage then calling it a day really isn't as helpful as it might sound. [1] https://en.wikipedia.org/wiki/Code_coverage#Basic_coverage_criteria https://en.wikipedia.org/wiki/Code_coverage#Basic_coverage_c... [2] https://en.wikipedia.org/wiki/Code_coverage#Modified_condition.2Fdecision_coverage https://en.wikipedia.org/wiki/Code_coverage#Modified_conditi...
- TempleOS 13y agoFunctional language people are like "Embarassingly parallel" enthusists.
- k_bx 13y agoWhile I agree that Haskell is a great language and everyone should learn it because it's fun to play with it, I think solution to author's problems (and as a current evolution step) would be something like Kotlin. It's Java-like language (no need to manage pointers), and it doesn't have null-pointer problem, but it's still high-level imperative OOP language. While many people say that they're code in Haskell is easier to read, since it has this "side-effect-free" guarantees, for me it seemed as not true for some time recently. In Haskell, when your code gets complicated (and starts to have some patterns you want to omit in typing), you start writing Monads. And when you start writing monads, your code gets harder to read since you need not only consider the code, but also keep (>>=) operator (all of them, if you use multiple monads combined via transformers) in your head for every pair of lines. Your code can suddenly have something like global variables (dynamic scoping) hidden in monad (as with State monad), it's flow can be changed dramatically and different other surprises.
- ExpiredLink 13y agoThe Haskell hype is already over the top.
- mercurial 13y agoHaskell gives you referential transparency and immutability guarantees, something you'll never get in Kotlin. That said, I agree that Haskell code is typically dense, and suffers from readability problems.
- k_bx 13y agoYeah, I'm just saying that referential transparency and immutability often turn into a lie, since this code: https://gist.github.com/k-bx/594a415a06fdd0fc3841 https://gist.github.com/k-bx/594a415a06fdd0fc3841 could easily put 2 different values into `a` on each line. Of course, technically everything is still correct, and getA still returns same result for each call.
- mercurial 13y agoEr, but that's not the same 'a', it's two different bindings with the same name, and you should get: foo.hs:3:9: Warning: This binding for `a' shadows the existing binding bound at ... when you compile with -Wall.
- dschiptsov 13y agoHaskell zealots are using the same nonsensical arguments that Java used, claiming to be a "safe" language where the compiler and static typing "eliminating common bugs". This is a naive meme, almost everyone telling to each other. First of all - no static typing system, whatever sophisticated it is, could protect from incompetence, lack of knowledge of underlying principles and plain stupidity. The dream that idiots will write a decent code will never come true, no matter how clever tools could be, just because it is impossible to write any respectable code without understanding hows and whys. But, for those who managed to understand the core ideas and concepts on which programming languages were based (immutability, passing by reference, properties of essential data-structures, such as lists and hash tables) it is possible to write sane and reasonable code even in C, leave alone CL or Erlang, and the type system will become a burden rather than advantage. So, Haskell is really good to master Functional Programming (which is much better to learn using old classic Scheme courses), to understand the ideas it rests on. To realize what is function composition, environments, currying, partial evaluation, closures, why they and when they are useful and handy, and how clear and concise everything (syntax and semantics) could be if you just stop here - just skip the part about Monads - they are just over hyped fanciness to show off. Learning Haskell after Scheme/CL really clarifies one's mind with realizations how the same foundation ideas work in a alien (static-typing) world, and how, everything is clean and concise, until you're starting messing everything up with "too advanced typing". Again, it is much better to learn the underlying ideas (why it is good to separate and pay special attention to functions that performing IO, what is recursive data structures and why null-pointers do exist in the first place) instead of stupid memes like "monads are cool" or "Haskell prevents bugs". The trick is that it is that dynamic languages with proper testing (writing tests before code) is not worse than this "static typing safety", and that the very word "safety" is just a meme.
- tome 13y agoThe benefit of Haskell for me is not that it allows "idiots" to write decent code, but that it allows very smart people to write decent code. It's much harder (in my experience) for very smart people to write decent code in C, C++, Python etc than in Haskell, simply because so much of their smartness is consumed by having to constantly think about what code might break their program.
- Confusion 13y agoEven in Haskell, you still need to test your code. The type system doesn't prevent double = (3*)
- skybrian 13y agoI skimmed so perhaps I missed something, but it looks like this is just the usual argument in favor of functional programming. I don't find it convincing because there's more to programming than getting the right output. Performance and memory usage are also important, and Haskell is extremely opaque in that respect. A strict language with optional laziness makes it much easier to reason about performance.