September 20, 2013 Re: Bartosz Milewski seems to like D more than C++ now :) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Thursday, 19 September 2013 at 22:46:09 UTC, Andrei Alexandrescu wrote:
> On 9/19/13 3:18 PM, Szymon Gatner wrote:
>> I had similar thoughts when watching GoingNaive 2013:
>> http://bartoszmilewski.com/2013/09/19/edward-chands/
>
> Nice piece.
>
>> I was more and more scared with every talk and now I am valualizing my
>> polymorphic types a'la Sean Parent
>
> That I think is sketchy advice.
>
>
> Andrei
What Sean Parent does in his value semantics talk is basically an interface adapter for a struct, wrapped in a struct providing implicit conversions.
By using structs by default and adapting them to interfaces as needed you get to have the smallest possible overhead on a single function call - either no virtual dispatch or 1 virtual dispatch. When writing adapters to interfaces you get 2 or more.
The other benefit is that approach is reducing dependencies you need to know about when you use the wrapper type - the approach would not work in D due to lack of argument dependent lookup so you need to write the adapter type (for a particular type you want to convert) yourself anyways. And I think it's better that way.
In the end the whole thing is just adapter design pattern applied to C++.
|
September 20, 2013 Re: Bartosz Milewski seems to like D more than C++ now :) | ||||
---|---|---|---|---|
| ||||
Posted in reply to QAston | On Friday, 20 September 2013 at 07:39:47 UTC, QAston wrote: > On Thursday, 19 September 2013 at 22:46:09 UTC, Andrei Alexandrescu wrote: >> On 9/19/13 3:18 PM, Szymon Gatner wrote: >>> I had similar thoughts when watching GoingNaive 2013: >>> http://bartoszmilewski.com/2013/09/19/edward-chands/ >> >> Nice piece. >> >>> I was more and more scared with every talk and now I am valualizing my >>> polymorphic types a'la Sean Parent >> >> That I think is sketchy advice. >> >> >> Andrei > > What Sean Parent does in his value semantics talk is basically an interface adapter for a struct, wrapped in a struct providing implicit conversions. > > By using structs by default and adapting them to interfaces as needed you get to have the smallest possible overhead on a single function call - either no virtual dispatch or 1 virtual dispatch. When writing adapters to interfaces you get 2 or more. > > The other benefit is that approach is reducing dependencies you need to know about when you use the wrapper type - the approach would not work in D due to lack of argument dependent lookup so you need to write the adapter type (for a particular type you want to convert) yourself anyways. And I think it's better that way. > > In the end the whole thing is just adapter design pattern applied to C++. http://dpaste.dzfl.pl/811800a5 |
September 20, 2013 Re: Bartosz Milewski seems to like D more than C++ now :) | ||||
---|---|---|---|---|
| ||||
Posted in reply to QAston | On Friday, 20 September 2013 at 07:39:47 UTC, QAston wrote: > On Thursday, 19 September 2013 at 22:46:09 UTC, Andrei Alexandrescu wrote: >> On 9/19/13 3:18 PM, Szymon Gatner wrote: >>> I had similar thoughts when watching GoingNaive 2013: >>> http://bartoszmilewski.com/2013/09/19/edward-chands/ >> >> Nice piece. >> >>> I was more and more scared with every talk and now I am valualizing my >>> polymorphic types a'la Sean Parent >> >> That I think is sketchy advice. >> >> >> Andrei > > What Sean Parent does in his value semantics talk is basically an interface adapter for a struct, wrapped in a struct providing implicit conversions. > > By using structs by default and adapting them to interfaces as needed you get to have the smallest possible overhead on a single function call - either no virtual dispatch or 1 virtual dispatch. When writing adapters to interfaces you get 2 or more. The struct was only an example, obviously you can use proper class implementations with data hiding etc. You will also get at least 1 virtual call because a "concept" is always an abstract class. That being said, I've been using this technique in other places in my code this far too, for example: my C++ ranges (yeah idea stolen from D) are templates so can be composed for free but there are abstractions like InputRange<> which "erase" (this word is stupid btw, no type is erased really) underlying type and provide convenient abstraction in places. I also use this technique for my std containers. Vector is a std::vector with polymorphic allocator, again it holds Allocator<> by value but underlying implementation is (often composition) of allocator templates. In allocator's case I do explicitly want container alloc to be polymorphic type so that my interfaces never depend on allocator type. Difference now for me is that I did sometimes turn value types into polymorphic types using this technique (like with allocators) but it never occurred to me that other wan can be beneficial too. > > The other benefit is that approach is reducing dependencies you need to know about when you use the wrapper type - the approach would not work in D due to lack of argument dependent lookup so you need to write the adapter type (for a particular type you want to convert) yourself anyways. And I think it's better that way. > > In the end the whole thing is just adapter design pattern applied to C++. Well Adapter is suppose to *change* interface to adapt to client needs so this is a bit of a strech to call it that here, but yeah, this technique called ("external polymorphism" idiom) can be used to adapt . This really just is a (smart)pointer with a full interface of underlying object (which also adds benefit or using "." instead of "->" on target). |
September 20, 2013 Re: Bartosz Milewski seems to like D more than C++ now :) | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | More from the blog post:
>It’s a common but false belief that reference counting (using shared pointers in particular) is better than garbage collection. There is actual research showing that the two approaches are just two sides of the same coin. You should realize that deleting a shared pointer may lead to an arbitrary long pause in program execution, with similar performance characteristics as a garbage sweep.<
That's an interesting paper, the symmetry between the two forms of garbage collection is a something to remember, but it doesn't contain benchmarks, you can't draw performance conclusions from that paper.
Bye,
bearophile
|
September 20, 2013 Re: Bartosz Milewski seems to like D more than C++ now :) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Szymon Gatner | On Friday, 20 September 2013 at 08:20:48 UTC, Szymon Gatner wrote: > The struct was only an example, obviously you can use proper class implementations with data hiding etc. You will also get at least 1 virtual call because a "concept" is always an abstract class. Direct use of the value type has no indirections. > That being said, I've been using this technique in other places in my code this far too, for example: my C++ ranges (yeah idea stolen from D) are templates so can be composed for free but there are abstractions like InputRange<> which "erase" (this word is stupid btw, no type is erased really) underlying type and provide convenient abstraction in places. I also use this technique for my std containers. Vector is a std::vector with polymorphic allocator, again it holds Allocator<> by value but underlying implementation is (often composition) of allocator templates. In allocator's case I do explicitly want container alloc to be polymorphic type so that my interfaces never depend on allocator type. > > Difference now for me is that I did sometimes turn value types into polymorphic types using this technique (like with allocators) but it never occurred to me that other wan can be beneficial too. > > Well Adapter is suppose to *change* interface to adapt to client needs so this is a bit of a strech to call it that here, but yeah, this technique called ("external polymorphism" idiom) can be used to adapt . This really just is a (smart)pointer with a full interface of underlying object (which also adds benefit or using "." instead of "->" on target). Well, I won't argue about naming, for me when a type is a wrapper which provides desired interface to that type is an adapter :P. I agree that this idiom is useful and I use it as needed too. |
September 20, 2013 Re: Bartosz Milewski seems to like D more than C++ now :) | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | On Friday, 20 September 2013 at 02:24:31 UTC, bearophile wrote:
> At Going Native 2013 there was a very good talk that among other things suggests to avoid raw loops in C++ code. But while this is good advice (so much that raw loops are becoming a bit of code smell for me), there are several situations where imperative loops keep being better for me. Explicit recursion is not always more readable and more easy to understand than imperative foreach loops.
I don't think his advice is to use recursion instead of loops. I believe the point was that raw loops can usually be replaced by something more generic (e.g. std::find, std::rotate, std::transform, etc.). The loop is just an implementation detail of a more high-level algorithm. Within those algorithms, it's ok to use loops.
|
September 20, 2013 Re: Bartosz Milewski seems to like D more than C++ now :) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Peter Alexander | On Friday, 20 September 2013 at 08:59:44 UTC, Peter Alexander wrote:
> On Friday, 20 September 2013 at 02:24:31 UTC, bearophile wrote:
>> At Going Native 2013 there was a very good talk that among other things suggests to avoid raw loops in C++ code. But while this is good advice (so much that raw loops are becoming a bit of code smell for me), there are several situations where imperative loops keep being better for me. Explicit recursion is not always more readable and more easy to understand than imperative foreach loops.
>
> I don't think his advice is to use recursion instead of loops. I believe the point was that raw loops can usually be replaced by something more generic (e.g. std::find, std::rotate, std::transform, etc.). The loop is just an implementation detail of a more high-level algorithm. Within those algorithms, it's ok to use loops.
While at the university, I got to read a paper about a HPC computer architecture with NUMA, that used Lisp as their systems programming language.
The functional programming approach was the only way to really take proper advantage of the hardware specific architecture.
This is why it is better to use algorithms instead of explicit loops, as they can be made to scale.
--
Paulo
|
September 20, 2013 Re: Bartosz Milewski seems to like D more than C++ now :) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Peter Alexander | Peter Alexander:
> On Friday, 20 September 2013 at 02:24:31 UTC, bearophile wrote:
>> At Going Native 2013 there was a very good talk that among other things suggests to avoid raw loops in C++ code. But while this is good advice (so much that raw loops are becoming a bit of code smell for me), there are several situations where imperative loops keep being better for me. Explicit recursion is not always more readable and more easy to understand than imperative foreach loops.
>
> I don't think his advice is to use recursion instead of loops. I believe the point was that raw loops can usually be replaced by something more generic (e.g. std::find, std::rotate, std::transform, etc.). The loop is just an implementation detail of a more high-level algorithm. Within those algorithms, it's ok to use loops.
In my comment I have packed too much in too little space. Let me try again:
In Haskell you usually don't use explicit recursion. You usually use higher order functions. But once in a while, for performance, to implement those HOFs or for other reasons you use explicit recursion.
In that Going Native 2013 talk it was argued that in C++ it's usually better to use STL algorithms instead of raw loops. But sometimes you have to use raw loops, when you implement those algorithms, and in other situations.
In D it's often better to use std.algorithm/range instead of raw foreach loops (despite probably unlike C++ in D a heavy use of ranges leads to a lower performance, even if you use the LDC2 compiler).
So in Haskell, C++ and D it's better to use higher iteration abstractions instead of raw recursion/iteration, but once in a while you have to use it. From what I've seen so far, in those cases I prefer to use explicit foreach iteration in D instead of explicit recursion in Haskell.
Bye,
bearophile
|
September 20, 2013 Re: Bartosz Milewski seems to like D more than C++ now :) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Peter Alexander | On Friday, 20 September 2013 at 08:59:44 UTC, Peter Alexander wrote:
> On Friday, 20 September 2013 at 02:24:31 UTC, bearophile wrote:
>> At Going Native 2013 there was a very good talk that among other things suggests to avoid raw loops in C++ code. But while this is good advice (so much that raw loops are becoming a bit of code smell for me), there are several situations where imperative loops keep being better for me. Explicit recursion is not always more readable and more easy to understand than imperative foreach loops.
>
> I don't think his advice is to use recursion instead of loops. I believe the point was that raw loops can usually be replaced by something more generic (e.g. std::find, std::rotate, std::transform, etc.). The loop is just an implementation detail of a more high-level algorithm. Within those algorithms, it's ok to use loops.
If only std algorithms took containers (by that I mean things that container for accepts too) as arguments (and not iterators)... even in the form of new functions like foreach_all, transform_all etc.
|
September 20, 2013 Re: Bartosz Milewski seems to like D more than C++ now :) | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | > From what I've seen so far, in those cases I prefer to use explicit foreach iteration in D instead of explicit recursion in Haskell.
Two other things to notice is that Haskell code seems to use too many different tiny functions and higher order functions, so it's hard for me to remember them all. The Haskell code starts to look like APL code. So using HOFs is useful, but beyond a certain number of them I prefer "raw coding".
Another thing to notice is that Haskell user defined operators sometimes hurt my ability to read code. This is composed with the precedent problem.
Bye,
bearophile
|
Copyright © 1999-2021 by the D Language Foundation