If I could swing it, I'd absolutely code everything in C#. Okay, maybe that fails the "fp-lang" bit, but my reasons are basically the same ;). Unity's finally getting around to covering the "consoles" bases, although I really wish they weren't using old buggy compilers and writing similarly buggy APIs. Maybe MonoGame will port to Xbox One eventually...
How would you implement pipe functions in Javascript? And I mean literally using the pipe operator, and not just chained. You can't, so you don't.
In Java? You can't, do you don't.
In C++ you can. You probably won't. You probably shouldn't so others can maintain your code in the future. But still, you can do very advanced things.
Thanks, pfultz2, for flexing C++'s muscles.
template<typename T> struct Shared : std::shared_ptr<T> {
template<typename U> struct is_compatible {
typedef char yes[1], no[0];
template<typename V> static yes& test1(typename std::enable_if<std::is_base_of<std::shared_ptr<T>, V>::value>::type*);
template<typename V> static no& test1(...);
template<typename V> static yes& test2(typename std::enable_if<std::is_base_of<element_type, typename V::element_type>::value>::type*);
template<typename V> static no& test2(...);
static constexpr bool value = sizeof(test1<U>(0)) == sizeof(yes) || sizeof(test2<U>(0)) == sizeof(yes);
};
And from the function binder: //value = true if R L::operator()(P...) exists
template<typename L> struct compatible {
template<typename T> static constexpr typename std::is_same<R, decltype(std::declval<T>().operator()(std::declval<P>()...))>::type exists(T*);
template<typename T> static constexpr std::false_type exists(...);
static constexpr bool value = decltype(exists<L>(0))::value;
};
template<typename L> function(const L& object, typename std::enable_if<compatible<L>::value>::type* = nullptr) { callback = new lambda<L>(object); }
And yet ... when actually using the library, it is a thing of beauty. struct TextEditor : Window {
MenuBar menuBar = {this};
Menu menuFile = {&menuBar, "File"};
MenuItem menuQuit = {&menuFile, "Quit"};
VerticalLayout layout = {this};
TextEdit editor = {&layout, Size{~0, ~0}};
TextEditor() {
StatusBar statusBar{this};
statusBar.setFont(Font::sans(8, "Bold")).setText("Line 1, Column 1");
HorizontalLayout findBar(&layout);
findBar.append(Label(&findBar, "Find:"));
findBar.append(LineEdit(&findBar).setBackgroundColor(Color::Yellow)
.onChange([&] { searchFor(text()); }));
findBar.append(Button(&findBar, "Clear")
.onActivate([&] { findBar.widget(1).setText(""); }));
menuQuit.onActivate(&Application::quit);
edit.onChange([&] { updateStatusBar(); });
}
};
There is no need for any memory management, or any usage of pointers. We build UIs, and everything gets automatically released safely when nothing is referring to it anymore. We can declare named objects that we can use later, or we can create dynamic objects and pop them right inside of other objects. We can destroy and unparent things whenever we want. And it's deterministic, reference-counted GC. No pauses for a tracer. No dynamic typing anywhere, all errors are at compile-time.I highly suspect that C++ is unreasonably complicated and that all of this rvalue-reference, variadic template, meta-programming, dynamic-casting polymorphism, is all just voodoo that isn't applicable to general programming. And yet, being able to do it gives me amazing expressive power to write awesome libraries that I could never hope to accomplish in another language.
Until I find a language that's even in the same ballpark as C++ in terms of performance, and offers similar expressiveness, it really doesn't even matter how bad C++ can be for library authors. There's no other viable option right now. D is the closest we have, but its complexity already rivals, if not exceeds, that of C++.
Edit: noticed you're using these in another place (`compatible` implementation), so perhaps there's a reason for a different approach?
Ideally you'd want to do enable_if< conditionA || conditionB >, but of course if one of the conditions fails to evaluate, the overload is ignored. So you have to split out the conditions and them merge them back together later on.
We could use true_type / false_type, but they have equivalent sizes. So unlike the function version that only needs one test and can just take the return type directly, the test at the end would then have to become std::is_same<decltype(test1<U>(0)), std::true_type>::value | std::is_same<decltype(test2<U>(0)), std::true_type>::value.
I still think we can do better than even this, so I'll have to keep working at it.
This sort of thing can be done a lot clear with functional constructs like compose and curry.
Common Lisp example (easily done with any language that supports higher order functions): (funcall (compose 'add-one 'add-one) 1)
And you can roll that a number of ways; adding in a curry of the identity of 1 if you just want a nullary function.
(funcall (compose 'add-one 'add-one (curry 'identity 1))
And if you are interested in typing less, maybe you are a dreadful typist...try Haskell.
1. It is something simple implemented in a complicated way.
2. It is something complicated implemented in a simple way.
3. It is something complicated implemented in a complicated way.
I believe this is an example of case 3, meaning even though it is something advanced, it should not take code that looks insane to express it.Futhermore, doing it by hand is not as simple, but still not very complicated. This just helps alleviate the boilerplate in defining pipable functions.
I'm not talking about using the pipe operator, I'm talking about implementing it. The C++ version is ridiculously complicated.
Let's compare your code with an analogous implementation in Common Lisp.
(defun pipe (val &rest fns)
(reduce (lambda (acc f)
(funcall f acc))
fns :initial-value val))
Just this allows for some pretty similar code: (pipe 99 #'1+ #'sqrt #'1-) which evaluates to 9.0
It is possible to implement it in C++, but the implementation winds up being ridiculously complicated. I just implemented something very similar in Common Lisp and it wound up being incredibly simple. Why doesn't C++ allow for a definition nearly as nice as the Common Lisp one?>But still, you can do very advanced things.
Why do you think "advanced" means the ability to overload operators? I'd personally define "advanced" to mean something to do with semantics, not syntax.
auto r = numbers().where([](int x) { return x > 2; }).select([](int x) { return x * x; });
Without the need and potential ambiguity that arises from overloading the pipe operator, you'd just use simple free functions instead and it'd just work.operator. is at the optimal precedence for method chaining, and has well understood behavior, so I think it'd be preferred over hijacking things like the bitwise-or operator.
But now that you mention it, an operator (such as pipe) with a lower precedence could actually be useful and spare having to jump back in code to open brackets.
Some of those syntax constructs are really hard to wrap my head around. In a lot of cases `x | f` is more readable than `f(x)` (especially when you want to chain several functions), but there's no way I can remember how to extend the language this way unless I'm a full-time C++ programmer. I don't feel the same in Haskell, for example.
Also, function composition should be similar to this, anyone can shed some light how it would be implemented? (I mean `x | f . g` would look awesome.)
They should have taken a hint from all of those coding standards coming from companies such as Google, which basically constrain the programmer to a small subset of the language features.
(Yeah, this depends on how good your debugger is. Visual Studio and XCode are pretty decent).
And for whatever reason, C++ debuggers and debug info formats don't seem to be doing a great job of keeping up with the language. Even basic stuff like nested function calls (as easily found with any smart pointer/iterator or array class...) tends not to work very well, to say nothing of single stepping in to std::function, or watching STL types, or using iterators or smart pointers in the watch window. So this necessarily means being a bit conservative about which features you use.
(You might have to go through the shipping-a-product process a couple of times before you really internalize this. But once you've learned it, it really does stick.)
I have to ask whether you've actually done any significant amount of programming in C++, both C++11 and pre-C++11.
I'd avoid Alexandrescu-style template trickery unless you really truly need it, though. You can write good C++ that more or less resembles Java, and that's fine.
That doesn't sound like good C++. Making everything a pointer or implementing polymorphism through pointers makes the code harder to reason about, and makes tighter coupling between interfaces and implementations.
You can still do this kind of horror with C++03. I'd know, I have!
Tests: http://sourceforge.net/p/libindustry/code/HEAD/tree/branches...
Code: http://sourceforge.net/p/libindustry/code/HEAD/tree/branches...
This was written shortly before I decided to stop fighting the language and simply use a better one for all of my hobby projects.
OT: That's also why I love the language - there are plenty of ways to solve a problem and C++, unlike other languages, does not force me to solve a problem "the right way".
While perhaps stated with slight hyperbole, this is actually a serious question. I don't see how, for example, these pipable functions give any additional real expressive power, and they seem incredibly hacky in how you have to build them. I simply don't understand why anyone would use this kind of thing. C makes sense; I can see an argument for C++03; this is madness.
auto r = select(where(numbers, [](int x) { return x > 2; }), [](int x) { return x * x; });
Because of nesting this can be hard to read(and even write). If we can make the function pipable, the above can be written like this: auto r = numbers | where([](int x) { return x > 2; }) | select([](int x) { return x * x; });
Which it is easier to see what this does. So this blogpost goes over how to implement an utility that will take care of the boilerplate involved in writing a function that can be pipable.It does take advantage of a lot of C++14 features to implement this such as such as vardiac templates, rvalue reference, generic lambdas, and auto type deduction, but those feature actually make it simpler. I have in the past implemented something similar for C++03 and the code looks awful with way too much noise.
> this is madness.
Why do you consider this madness?
Maybe my mind has already been warped by C++.
I think it's fascinating to see how the language can be extended. On the other hand I would not want to be the poor bastard stuck maintaining either construct after discovering some compiler edge case. If I've learned anything it's that just because you can do something doesn't mean you should.
The issue with extending operators in this fashion is that you're violating the principle of least surprise. | is an operator that should only really do exactly what it always does--bitwise or. If composition of functions is fundamentally analogous to taking the bitwise or of two values then I would concede that your second syntax is a bit clearer.
Personally I feel that this is not the case.
It is really common for the `|` operator to be used for piping functions together. There are several libraries that support it: [Boost.Range](http://www.boost.org/doc/libs/1_56_0/libs/range/doc/html/ind...), [PStade Oven](http://p-stade.sourceforge.net/oven/doc/html/index.html), [Linq](http://pfultz2.github.io/Linq/), and [Streams](http://jscheiny.github.io/Streams/). Plus, its even being proposed as part of the ranges library in future C++: https://github.com/ericniebler/range-v3/blob/master/doc/D412...
So just as the bit shift operators in C++ also have the meaning of streaming in and out, the bitwise or also has the meaning of pipe, so this is should not at all be a suprise.
At any rate, the thing that's strange about it is how verbose and hackish everything is. It's obvious the language wasn't designed to do anything like this, so the only way you can incorporate features like this is with unnecessarily obtuse and complex blocks of code.
Actually, pipable is very similiar to haskell's `$` function. So a similiar thing is done to improve readability of chaining functions in functional languages as well.
> At any rate, the thing that's strange about it is how verbose and hackish everything is. It's obvious the language wasn't designed to do anything like this, so the only way you can incorporate features like this is with unnecessarily obtuse and complex blocks of code.
How is the code obtuse and complex? I am using fairly straightforward techniques. I understand that I am working around the lack of perfect capturing for lambdas, but it is not complicated or clever the workaround.
Thanks
E.g. numbers.where().select();
http://www.boost.org/doc/libs/1_56_0/libs/range/doc/html/ran...