8 ms·
Haskell's effect on my C++ : exploit the type system
- pmr_ 15y agoIf your only example to show C++ verbosity fails to compile and is also patently wrong, you are not making a good point. (The constructor is private, the variable naming prevents short accessors such as Rectangle::width()). Better: struct Rectangle { double x,y, width, length; }; int main() { Rectangle r = {0., 0., 10., 10. }; return 0; } This even compiles in C++03. If you really insist on private members, you will need initializer lists and some changed member names. The small example error has been worked around with for years by simply placing the constant on the left side of the comparison. Not pretty, but effective. I agree with the general point, though. A functional style can make C++ much more efficient. It is also the only way to do template meta-programming and everybody who tries to go into that direction can benefit from learning Haskell.
- gtani 15y agobookmarked: http://bartoszmilewski.com/2011/07/11/monads-in-c/ http://bartoszmilewski.com/2011/07/11/monads-in-c/ http://bartoszmilewski.com/2010/11/29/understanding-c-concepts-through-haskell-type-classes/ http://bartoszmilewski.com/2010/11/29/understanding-c-concep...
- pmr_ 15y agoI should have mentioned those articles. They are a great example of Haskell influencing a C++ programmer. Although this article might show what the average C++ programmer thinks `using the type system`, `verbosity` and `influencing their style` mean.
- gtani 15y agoAlso found a def of type classes that i like: automatic instantiation + constraint propagation http://blog.garillot.net/post/15147165154/every-single-freshman-cs-question-in-80-lines-of-scala http://blog.garillot.net/post/15147165154/every-single-fresh...
- cpeterso 15y agoIf you replace Rectangle r with struct Rectangle r, your example even compiles in C89. :)
- apl 15y ago> Although the newcomer C# was only introduced ten years > ago, there is little in the language to reflect the > thirty years of research into programming languages since > C++ was introduced. Not a huge fan of C#, but he couldn't have picked a worse example for language stagnation.
- peregrine 15y agoI can say form experience that C# has several interesting features built on research from Haskell, just gotta probe the edges a bit. And once you probe the edges too hard your code becomes worthless for the standard blub programmer who'd prefer not have to learn the syntax.
- ot 15y agoAgreed, especially considering the amount of cross-pollination there is between C# and Haskell, possibly through F#. The people that develop F# and those who develop a large part of Haskell drink coffee from the same coffee machine.
- hythloday 15y agomap and fold are available in C++, though they're called std::transform (in <algorithm>) and std::accumulate (in <numeric>). Totally agree with the author's point, the C++ I write nowadays is heavily influenced by my Scala/Haskell/Lisp exposure and is much better for it.
- evincarofautumn 15y agoYep. <algorithm>, <numeric>, <functional>, and <iterator> are all incredibly useful, yet so underused.
- Drbble 15y agoDoes 'accumulate' work on non-'numeric' types? Part of the beauty of Haskell is the aggressive effort to define each function on the most general type it applies to (and then specialize to concrete types during compilation, a la C++, when performance is requested via a pragma), leading to a rich hierarchy of tiny type classes (type classes are similar to C++ abstract classes)
- hythloday 15y agoYeah, it's a real fold, just misleadingly named: int main() { string s[] = { "a", "b", "c" }; // will print "abc" cout << accumulate(s, s+3, string(""), [](string l, string r) { return l+r; }); return 0; } I think a closer C++ equivalent for typeclasses are specialized template functions--for example, if I define: template<InputIterator> string accumulate<string>(InputIterator first, InputIterator last, string init); then my call to accumulate(s, s+3, string("")) will use that specialization.
- rquirk 15y agoDo people really still make the `=` vs `==` mistake? GCC -Wall has had "warning: suggest parentheses around assignment used as truth value" since as long as I can remember.
- alexchamberlain 15y agoYou make a good point, but the mistake is still made. Semantic correctness has other advantages, such as more optimisation when possible.
- jerf 15y agoThat's an easy and one might say picturesque example to give, but there are many variants, such as an if statement that in one branch turns out to change the value in that variable, then later on you forget about it and use it incorrectly. Or it gets passed into something taking it as a reference which unexpectedly modifies it, or so on and so forth. All of these are a lot harder to give examples of in a blog post because they don't really hit you in bite-sized chunks of example code. By definition, you're supposed to understand everything going on in an example chunk of code, so it's hard to show the problems that can emerge with hard-to-understand code.
- udp 15y agoIt's the one mistake that everyone seems to cite when complaining about C, and I've literally never seen it happen in all my years of programming. Right up there with fall-through cases.
- pnathan 15y agoI have made both mistakes.
- slowpoke 15y agoI use Yoda Conditions[1] in C-oid languages, so the error would be visible even without warnings (because the program would refuse to compile). [1]: http://stackoverflow.com/questions/2349378/new-programming-jargon-you-coined/2430307#2430307 http://stackoverflow.com/questions/2349378/new-programming-j...
- Blunt 15y agoyou don't pick the language. The language picks you!
- deleted 15y ago[deleted]
- landtuna 15y agoThat first example is terrible. C doesn't guarantee that the arguments to a macro are evaluated only once. At best, you might be doing the computation twice. At worst (if the function has internal state), you could get different answers for the comparison and the assignment. The non Haskell-style is better!
- cageface 15y agoBetter to use the std::max template function.
- _delirium 15y agoI also find the non-Haskell style much more readable, but I've spent a lot more time reading C code than Haskell code, so that could just be idiomatic familiarity. To me it's clearer that it's doing: "use this value, except that if it's below 0, clamp it to 0". The MAX() reads to me semantically strangely, since I tend to view "max" as idiomatic when selecting between values, like max(left_child,right_child), but not as an idiomatic way of clamping values. It works, of course, just takes me longer to unpack, the way using short-circuit booleans for control flow takes me a second to unpack. If it's a case where you can retrieve the value essentially free w/o side effects (as opposed to the result of a computation), one idiomatic alternative is: const int a = foo >= 0 ? foo : 0;
- Drbble 15y agoDo note that calling functions with the unsafe side effects is not Haskell-style, as that expression is not even possible in Haskell without literally typing word "unsafe" into your program at least twice. It is supposedly "Haskell-style" only in the OP's incorrect description.
- cageface 15y agoI've been reading predictions of the immanent breakthrough of FP into the mainstream for over 15 years now. I'm still waiting.
- tomjen3 15y agoOne of the more clever not so well known C++ type system hacks I have seen is the way Ogre3D (a graphics engine for games) handle Degrees versus radians. Basically they have a class for each and have overloaded the operators so that you can mix and match operations freely (e.g you get a radian result from some computation which you can then add twenty degrees to simply by saying + Degree(20) and have the C++ type system automatically convert back and forth. And since non virtual classes (think Java classes where all members are final) take no memory and can be trivially inline there is no runtime performance hit at all. It is a joy to use to.
- Drbble 15y agoDo you mean "members are final" or "classes are final"? A final member is constant, which has nothing to do with inheritance. A final class has no subclasses.
- tomjen3 15y agoAll members are final, and it inherits from nothing -- that way there is no need to store any table of methods with each object as so the size requirement is limited to a single table.
- deleted 15y ago[deleted]
- saalweachter 15y agoWhile modern C++ (or Java, for that matter) may not reflect 30 years of programming language research, it certainly has benefited 30 years of programmer feedback and development. In C++11, the language is unaltered from the perspective of a PL researcher, its type systems unchanged, but wildly improved from the perspective of a programmer. Initializer lists, auto, lambdas, and for-each loops were added, all of which make the language more compact in ways which most programmers will appreciate. Futures will do for multi-threading in C++ what RAII did for resource management. Programmers are not computer scientists. From the perspective a programmer, the big three languages -- C++, Java, Python -- are quite alive and changing in ways which allow us to write better, more interesting programs. Even if they are built on thirty year old type systems.
- drt1245 15y agoint a = f(); if (a < 0) a = 0; has been replaced with const int a = MAX(0,f()); I believe the latter will generally result in 2 calls to f() (unless it returns less than zero), which could cause undesired behavior or just be inefficient. Of course, it depends on what f() does. Assuming MAX(0,f()) expands to something like this: if( f() < 0 ) a = 0 else a = f()
- mikeash 15y agoIf MAX evaluates f() twice, then it is broken. It's trivial to write a version that only evaluates its arguments once in C++ using templates, and in C using common extensions that allow for macros with multiple statements that act as an expression.
- evincarofautumn 15y agoIt was a poor choice on the part of the author to use the macro-looking MAX() in his example. There is in fact a standard max() function in the <algorithm> header that does not suffer from silly macro-expansion problems: template<class T> const T& max(const T& a, const T& b) { return a < b ? b : a; } template<class T, class Predicate> const T& max(const T& a, const T& b, Predicate p) { return p(a, b) ? b : a; } And your definition of MAX() is wrong, not just because of multiple evaluation of the arguments. The “conventionally wrong” MAX() would be this: #define MAX(a, b) ((a) < (b) ? (b) : (a))
- drt1245 15y agoThe problem is a loss of verbosity. The next person looking at the code isn't necessarily going to know how MAX is defined, and saving 1 line of code isn't worth making it less clear. That said, for me, your code would be clear, as not being in all caps, I would assume it's a function and therefore doesn't have the same weakness. As someone else pointed out, there's no way you can look at MAX(0,f()) and know that it's enforced that f() is only used once within the macro.
- eps 15y ago> int a = f(); > if (a < 0) a = 0; > has been replaced with > const int a = MAX(0,f()); Going by MAX capitalization, it must be a macro, in which case the code is wrong. I am guessing this is a synthetic example, but still - try and get the basics right.
- evincarofautumn 15y agoTechnically, the code is wrong only if f() is not pure.
- eps 15y agoYes, I know, but there is no way to enforce it. This a classic "noob" mistake with macros - using a macro argument more than once.
- dkersten 15y agoThat's why in C++ people seem to generally recommend inline functions instead. Not only do the arguments only get evaluated once, but the whole thing is type safe too. Of course, people still misuse macros.
- CGamesPlay 15y agoI feel pretty safe assuming a pure function in a blog post about the advantages of immutability, really.
- michaelochurch 15y agoMy somewhat biased stance: functional programming languages make you progress as a programmer quickly. Becoming great requires deliberate practice and intrinsically hard problems. So you learn programming and CS about 5-10 times as fast in a functional language. This is part of why enterprise Java/C++ devs plateau at a skill level of 1.2 (even into their gray-haired years) while ML and Haskell programmers can reach 1.5 in a few years and generally cross 2.0 if they make a career out of software. (For reference, here's where I explain that scale: http://michaelochurch.wordpress.com/2012/01/26/the-trajectory-of-a-software-engineer-and-where-it-all-goes-wrong/ http://michaelochurch.wordpress.com/2012/01/26/the-trajector... ; it's a 0.0-to-3.0 scale and 1.5 is what most of us would consider a seriously good programmer.) So if you want to grow as an engineer and computer scientist, learn these languages. You'll learn a lot fast, and not just about one programming paradigm (FP) but about complexity and how to manage it. You'll just learn a lot more about software because the way to learn software is to write it and one person can actually accomplish significant things in these languages. Java and C++ demote the individual contributor: "commodity" developers crank out classes and a more senior "architect" figures out how to bolt them together. That said, these languages won't improve your performance in typical Java and C++ jobs. Six months of practice for exposure might give you some new ideas and make your code and performance better. Beyond that, they might diminish it. Why? Because you start hating these languages (Java and C++) and the accidental complexity they throw at you, which starts to look foreign and unnecessary. You get to a point where your own code (if you use the idioms of these languages, and you probably should, because it's not just about you) starts to look like someone else's. When you start hating your own code, disengagement sets in and it gets ugly fast. I can "hack" Java and C++ but I certainly wouldn't do a major project in them at this point. My Java and C++ are uninspired now that I know better languages. Often, the only way I can write decent Java is to write it first in Scala (which I like, a lot) and transliterate the solution. Whether this produces "good" Java I don't even know. Also, I really believe that C++ is the 21st-century COBOL. Java was gunning for that distinction, but Scala and Clojure gave it an assembly-like status, as the lowest-level language on the JVM (which will remain important for at least 15 years because of Scala and Clojure).
- CJefferson 15y agoI agree with one side of your message. There are very few people who have just learnt functional programming. I have met a few in academia, and they tend to be as "bad" as the pure Java/C++ programmers, but in a different way. They have trouble understanding how their code will boil down to CPU instructions, and so have trouble with performance, particularly (unsuprisingly) mutating algorithms, which are vital for performance in some situations. After I have defined a high-performance algorithm, they often want to modify it to make it "more beautiful" (which means functional) and in the process destroy the performance. Also, I had quite a lot of exposure to Haskell, and I haven't found C++ hate. I got some Haskell hate, because doing some things (mutating algorithms and bit fiddling) were really, really hard, while I found most of the things I loved from Haskell I could do in C++, with care. I work on low-level algorithm design in A.I., where performance is everything and there are many interlocking, low-level and complex algorithms and data structures. I know that other problems might well have very different characteristics. For example, I have no real ideal how Haskell vs C++ would compare for the average web app for example (or of course, something like Ruby/Python which seem to be much more popular).
- ajax77 15y agoI can sympathize; I too use C++ by day, and have become increasingly bitter/jealous/frustrated as I've spent more time with Haskell by night. To the point that, I've started writing several libraries that enable in C++ several of the features I miss most in Haskell. In particular, I've missed the natural syntax of higher order functions, e.g., currying and functional composition, as well as the pithy expressiveness of Prelude vs the STL. With C++11, we can have these things, and while Boost gives us a wealth of functionality, it's hardly the most natural (and lightweight) library. fc (functional composition) (https://github.com/jdduke/fc https://github.com/jdduke/fc) and fpcpp (functional programming with C++)(https://github.com/jdduke/fpcpp https://github.com/jdduke/fpcpp) are my recent (very much work-in-progress) efforts towards this end. With fc, one can naturally compose functions, lambdas and function objects, e.g., given composable functions f, g, h, write auto fgh = f + g + h; or, auto fgh = compose(f, compose (h,g));, and then call it as fgh(a,b,c); fpcpp enables syntax like let pi = [](unsigned samples) -> double { typedef std::pair<double,double> point; let dxs = map([](const point& p) { return p.first*p.first + p.second*p.second; }, zip(take(samples, rand_range(-1.0,1.0)), take(samples, rand_range(-1.0,1.0)))); return 4.0 * filter([](double d) { return d <= 1.0; }, dxs).size() / dxs.size(); } EXPECT_NEAR(pi(10000), 3.14); In writing fc and fpcpp, I've actually become less and less concerned for the future of C++; having used a good number of the C++11 features available, I've regained a good deal of my passion for C++ programming. Really, C++ needs a more expansive and natural standard library, iterators are powerful but are NOT the answer.
- cpeterso 15y agoC++11 adopted strongly-typed enums, but I wish it also added strongly-typed typedefs. I've written (and found others') template classes to implement something similar, but the implementation is surprisingly (?) verbose and may introduce code bloat or runtime overhead. The compiler could implement this with no overhead! C++11's strongly-typed enums look like: enum class Enumeration { Val1, Val2}; enum class Enum2 : unsigned int {Val1, Val2}; A strongly-typed typedef could use similar "declare a class deriving from a native type" syntax: class Celcius : double; // something like this? typedef Fahrenheit : double; // or maybe this? Celcius celcius = 0f; Fahrenheit fahrenheit = celcius; // ERROR