Date: Fri, 7 Feb 2020 21:29:24 +0000
(Sending again because I forgot to reply to all)
Yes, this is precisely what I'm looking for. Through that, you can
construct an effective pipeline without the bootstrap.
Take this, for example: https://godbolt.org/z/YMtsE_ - a pipeline that uses
callbacks to gather stats from halves and quarters of a range of numbers,
and output the source numbers. Nothing complex going on, but through
Godbolt's handy dual rainbow colour scheme it is clear that jumps are
minimised through inlining, such that the individual components seemingly
run sequentially from each branch in their respective sources.
I have two issues with this approach.
First, the compilation of an entire processing stream by forming one object
formed of separate concatenated objects is simply not as intuitive as a
line-by-line instructions. I love functional programming as much as the
next person, but I feel like it's taking over the language and making
simple concepts inaccessible. I feel like the benefit that functional style
gives us in terms of powerful type manipulation could just as easily be
provided in imperative style, just as it is in python, while also making
the types well defined at each point.
Second, the bootstrap. Oh the bootstrap.
Here's how I'd rather have the code:
======
struct StatsCalculator{
unsigned integer_total = 0;
double double_total = 0;
unsigned integer_count = 0;
unsigned double_count = 0;
void update(unsigned const& input){
integer_total += input;
++integer_count;
}
void update(double const& input){
double_total += input;
++double_count;
}
void print(char const* title){
printf("=== %s ===", title);
printf("Average of the integers: %.3f (of %u samples)\n",
static_cast<double>(integer_total) / integer_count, integer_count);
printf("Average of the doubles: %.3f (of %u samples)\n",
double_total / double_count, double_count);
}
};
var divider(unsigned input, unsigned divisor){
if(input % divisor == 0){
return input / divisor;
}else{
return static_cast<double>(input) / divisor;
}
}
int main(){
StatsCalculator half_stats, quarter_stats;
for(unsigned i = 0; i < 100; ++i){
with(var h = halver(i)){
half_stats.update(h);
}
with(var q = quarterer(i)){
quarter_stats.update(q);
}
printf("Processed integer %u\n", i);
}
half_stats.print();
quarter_stats.print();
}
On Fri, Feb 7, 2020 at 7:56 PM Михаил Найденов <mihailnajdenov_at_[hidden]>
wrote:
>
>
> On Fri, Feb 7, 2020 at 8:43 PM Jake Arkinstall <jake.arkinstall_at_[hidden]>
> wrote:
>
>> On Fri, 7 Feb 2020, 18:12 Михаил Найденов, <mihailnajdenov_at_[hidden]>
>> wrote:
>>
>>>
>>>
>>> On Fri, Feb 7, 2020 at 7:08 PM Lee Howes <xrikcus_at_[hidden]> wrote:
>>>
>>>> Well the idea would be that you could unroll it too:
>>>> for co_await(auto [key, value] : dictionary) {
>>>> foo(key, value);
>>>> }
>>>> (I forget what we intended for for co_await syntax, but it's not in
>>>> there yet anyway so let's go with it)
>>>>
>>>> The code would end up instantiating foo over all the key/value type
>>>> combinations. The coroutine callback logic for dictionary iteration would
>>>> yield each key/value pair by callback directly without any variant involved.
>>>>
>>>> This is all speculation though, because actually implementing this and
>>>> convincing people that the reader will not be confused is still a bit of a
>>>> step.
>>>>
>>>
>>> This is template for, I believe.
>>>
>>
>>
>
>> For the case of iterating over a static dictionary, template for would do
>> well.
>>
>> It doesn't, however, solve the issue of iterating over types yielded
>> based on runtime values, such as a sequence of events generated by an input
>> string. It would instead require the sequence of types to be known at
>> compile time. A template for in this context would allow branching to
>> different instantiation of the for block depending on the type generated on
>> each iteration.
>>
>
> yeah, got ticked, because dictionary was a variable.
>
> In any case, coroutines aside, if just the indirection is the issue, we
> should really see what optimization will be possible with Patter Matching +
> (language-or-not) variant.
>
> Consider for example
>
> variant<uint, float> halve(unsigned i){
> if(i % 2 == 0){
> return i / 2; // unsigned path
> }else{
> return i * 0.5; // double path
> }
> }
>
> void caller(uint i) {
> inspect halve(i) Numeric x => /*do work*/;
> }
>
> If all cases are handled, and if halve is inline, the compiler will be
> able to transform the caller
>
> void caller(uint i) {
> if(i % 2 == 0){
> unsigned x = i / 2; /*do work*/ ;
> }else{
> float x = i * 0.5; /*do work*/ ;
> }
> }
>
> The fact that the compiler knows about visitation naively will surely lead
> to some powerful new optimization possibilities, without additional
> language machinery.
>
>
>
>
>
>
Yes, this is precisely what I'm looking for. Through that, you can
construct an effective pipeline without the bootstrap.
Take this, for example: https://godbolt.org/z/YMtsE_ - a pipeline that uses
callbacks to gather stats from halves and quarters of a range of numbers,
and output the source numbers. Nothing complex going on, but through
Godbolt's handy dual rainbow colour scheme it is clear that jumps are
minimised through inlining, such that the individual components seemingly
run sequentially from each branch in their respective sources.
I have two issues with this approach.
First, the compilation of an entire processing stream by forming one object
formed of separate concatenated objects is simply not as intuitive as a
line-by-line instructions. I love functional programming as much as the
next person, but I feel like it's taking over the language and making
simple concepts inaccessible. I feel like the benefit that functional style
gives us in terms of powerful type manipulation could just as easily be
provided in imperative style, just as it is in python, while also making
the types well defined at each point.
Second, the bootstrap. Oh the bootstrap.
Here's how I'd rather have the code:
======
struct StatsCalculator{
unsigned integer_total = 0;
double double_total = 0;
unsigned integer_count = 0;
unsigned double_count = 0;
void update(unsigned const& input){
integer_total += input;
++integer_count;
}
void update(double const& input){
double_total += input;
++double_count;
}
void print(char const* title){
printf("=== %s ===", title);
printf("Average of the integers: %.3f (of %u samples)\n",
static_cast<double>(integer_total) / integer_count, integer_count);
printf("Average of the doubles: %.3f (of %u samples)\n",
double_total / double_count, double_count);
}
};
var divider(unsigned input, unsigned divisor){
if(input % divisor == 0){
return input / divisor;
}else{
return static_cast<double>(input) / divisor;
}
}
int main(){
StatsCalculator half_stats, quarter_stats;
for(unsigned i = 0; i < 100; ++i){
with(var h = halver(i)){
half_stats.update(h);
}
with(var q = quarterer(i)){
quarter_stats.update(q);
}
printf("Processed integer %u\n", i);
}
half_stats.print();
quarter_stats.print();
}
On Fri, Feb 7, 2020 at 7:56 PM Михаил Найденов <mihailnajdenov_at_[hidden]>
wrote:
>
>
> On Fri, Feb 7, 2020 at 8:43 PM Jake Arkinstall <jake.arkinstall_at_[hidden]>
> wrote:
>
>> On Fri, 7 Feb 2020, 18:12 Михаил Найденов, <mihailnajdenov_at_[hidden]>
>> wrote:
>>
>>>
>>>
>>> On Fri, Feb 7, 2020 at 7:08 PM Lee Howes <xrikcus_at_[hidden]> wrote:
>>>
>>>> Well the idea would be that you could unroll it too:
>>>> for co_await(auto [key, value] : dictionary) {
>>>> foo(key, value);
>>>> }
>>>> (I forget what we intended for for co_await syntax, but it's not in
>>>> there yet anyway so let's go with it)
>>>>
>>>> The code would end up instantiating foo over all the key/value type
>>>> combinations. The coroutine callback logic for dictionary iteration would
>>>> yield each key/value pair by callback directly without any variant involved.
>>>>
>>>> This is all speculation though, because actually implementing this and
>>>> convincing people that the reader will not be confused is still a bit of a
>>>> step.
>>>>
>>>
>>> This is template for, I believe.
>>>
>>
>>
>
>> For the case of iterating over a static dictionary, template for would do
>> well.
>>
>> It doesn't, however, solve the issue of iterating over types yielded
>> based on runtime values, such as a sequence of events generated by an input
>> string. It would instead require the sequence of types to be known at
>> compile time. A template for in this context would allow branching to
>> different instantiation of the for block depending on the type generated on
>> each iteration.
>>
>
> yeah, got ticked, because dictionary was a variable.
>
> In any case, coroutines aside, if just the indirection is the issue, we
> should really see what optimization will be possible with Patter Matching +
> (language-or-not) variant.
>
> Consider for example
>
> variant<uint, float> halve(unsigned i){
> if(i % 2 == 0){
> return i / 2; // unsigned path
> }else{
> return i * 0.5; // double path
> }
> }
>
> void caller(uint i) {
> inspect halve(i) Numeric x => /*do work*/;
> }
>
> If all cases are handled, and if halve is inline, the compiler will be
> able to transform the caller
>
> void caller(uint i) {
> if(i % 2 == 0){
> unsigned x = i / 2; /*do work*/ ;
> }else{
> float x = i * 0.5; /*do work*/ ;
> }
> }
>
> The fact that the compiler knows about visitation naively will surely lead
> to some powerful new optimization possibilities, without additional
> language machinery.
>
>
>
>
>
>
Received on 2020-02-07 15:32:12