Hacker News new | ask | show | jobs
The Functor Pattern in C++ (fpcomplete.com)
63 points by PopaL 5084 days ago
3 comments

C++ is a powerful language, but with awful syntax and semantics that can discourage experimentation. Since I started using Haskell just over a year ago, my C++ code is much improved—because I can figure things out quickly in Haskell, then translate them to (somewhat write-only) C++. That I think is a large part of what people mean when they say “Haskell makes me a better programmer”. Not only is it a good language in its own right, but it also helps you prototype software that you need to implement in another language for reasons other than language power.
"C++ is a powerful language, but with awful syntax and semantics that can discourage experimentation."

Then you should try out D. Bartosz Milewski in fact has been involved in D development for a long time. It certainly makes it easier to apply a lot of the FP techniques.

I have experimented a little bit with D, and really like some of the things in the standard library, but Haskell is already serving as my replacement for C++. D is a viable alternative to C++ for most purposes, though.

I’m actually working on a compiler for a high-performance functional language, to serve in those cases when I would want to go back to C++…

I'm working on-and-off on something similar, would you excuse a few questions?

Is it statically typed? Do you have ADT support and/or an object system? GC / refcounting or manual memory management?

How are you implementing bind() and currying in a compiler? I'm targeting x86 and my approach is to make little stubs on the heap that push a single parameter, then i can throw them around as ordinary function pointers (lambda lifting every single parameter rather than looking up things through environment pointers at runtime). The problem with this is that i end up with a huge amount of small fixed-size objects on the heap, which is a little less than ideal but at least i can write a special allocator for that case.

EDIT: Upon a little background reading (and your illuminating february blog post on concatenative languages) i realise you're probably referring to Kitten, in which case i'm not sure any of my current questions are remotely applicable.

I am moving from dynamic to static typing. Unfortunately the `compose` primitive has a rank-2 type in the general case, so you need explicit type annotations for many higher-order functions. I am supporting ADTs, which are basically identical to quotations under the hood. There will be an object system in the standard library once I figure out a good object model. Memory management is done through plain old refcounting. Some programs may benefit from waiting for the next push before deallocating popped values, but I have yet to profile to find out.

As you may have realised, currying doesn’t really apply to concatenative languages because they lack application. The analogue of partial application is deferred composition, i.e., just concatenating quotations:

    [1] [+] compose  [1 +]  =
I’ve been working on the language actively, but haven’t pushed code lately. Do watch the repo[1] if you’re interested, though, and link me to yours. I also think I know you from #esoteric or something? My memory sucks and I haven’t been on there lately.

[1] https://github.com/evincarofautumn/kitten

You had me until "somewhat write-only." Sounds like you are poisoning your code-base with square functional pegs in round C++ holes...
Hardly. Given a choice between complex imperative code and complex functional code, I’ll take the latter. It’s just that the resultant C++ is somewhat brittle and not as thoroughly verified as the Haskell version. I would say the same thing if I wrote something in Coq, proved it correct, then ported it to Haskell.
std::function is really nice. I've started using it everywhere.
If only it didn’t use dynamic allocation…ah well. The value is worth the cost.
I don't think std::function does any dynamic allocation when you initialize it with simple functions. When you initialize it with function objects or lambda expression, most, if not all implementations use the small object optimization to avoid dynamic allocation when possible.
With function objects the GNU std::function does do dynamic allocation. Even for objects as small as an empty struct.
In this case it's probably unnecessary. It's probably better to template these higher-order functions on their function arguments and just return the lambda itself instead of returning it in a function wrapper.
...or why types and the lack of anonymous functions combine to make a language painful, ugly, and annoying.
Types certainly don't.