July 01, 2013
On Monday, 1 July 2013 at 04:19:51 UTC, Timon Gehr wrote:
> On 07/01/2013 05:44 AM, JS wrote:
>> On Monday, 1 July 2013 at 01:56:22 UTC, Timon Gehr wrote:
>>> ...
>>> The described strategy can easily result in non-termination, and which
>>> template instantiations it performs can be non-obvious.
>>>
>>> auto foo(T)(T arg){
>>>    static if(is(T==int)) return 1.0;
>>>    else return 1;
>>> }
>>>
>>> void main(){
>>>    auto x;
>>>    x = 1;
>>>    x = foo(x);
>>> }
>>
>> Sorry,
>
> That's fine.
>
>> it only results in non-termination if you don't check all return
>> types out of a function.
>
> Why is this relevant? I was specifically responding to the method lined out in the post I was answering. There have not been any other attempts to formalize the proposal so far.
>
>> It is a rather easy case to handle by just
>> following all the return types and choosing the largest one.
>
> That neither handles the above case in a sensible way nor is it a solution for the general issue. (Hint: D's type system is Turing complete.)
>
>> No big deal...  any other tries?
>
> That's not how it goes. The proposed inference method has to be completely specified for all instances, not only for those instances that I can be bothered to provide to you as counterexamples.

well duh, but it is quite a simple mathematical problem and your counter-example is not one at all.

For a statically typed language all types must be known at compile time... so you can't come up with any valid counter-example. Just because you come up with some convoluted example that seems to break the algorithm does not prove anything.

Do you agree that a function's return type must be known at compile time in a statically typed language? If not then we have nothing more to discuss... (Just because you allow a function to be compile time polymorphic doesn't change anything because each type that a function can possibly return must be known)
July 01, 2013
On Monday, 1 July 2013 at 06:38:20 UTC, JS wrote:
> well duh, but it is quite a simple mathematical problem and your counter-example is not one at all.
>
> For a statically typed language all types must be known at compile time... so you can't come up with any valid counter-example. Just because you come up with some convoluted example that seems to break the algorithm does not prove anything.
>
> Do you agree that a function's return type must be known at compile time in a statically typed language? If not then we have nothing more to discuss... (Just because you allow a function to be compile time polymorphic doesn't change anything because each type that a function can possibly return must be known)

As a compiler implementer, Timon is probably way more competent than you are on the question. You'll get anything interesting to add by considering you know better.

The type of problem he mention are already present in many aspect of D and makes it really hard to compile in a consistent way accross implementations. Adding new one is a really bad idea.

If you don't understand what the problem is, I suggest you to study the question or ask questions rather than try to make a point.
July 01, 2013
On Monday, 1 July 2013 at 06:51:53 UTC, deadalnix wrote:
> On Monday, 1 July 2013 at 06:38:20 UTC, JS wrote:
>> well duh, but it is quite a simple mathematical problem and your counter-example is not one at all.
>>
>> For a statically typed language all types must be known at compile time... so you can't come up with any valid counter-example. Just because you come up with some convoluted example that seems to break the algorithm does not prove anything.
>>
>> Do you agree that a function's return type must be known at compile time in a statically typed language? If not then we have nothing more to discuss... (Just because you allow a function to be compile time polymorphic doesn't change anything because each type that a function can possibly return must be known)
>
> As a compiler implementer, Timon is probably way more competent than you are on the question. You'll get anything interesting to add by considering you know better.
>
> The type of problem he mention are already present in many aspect of D and makes it really hard to compile in a consistent way accross implementations. Adding new one is a really bad idea.
>
> If you don't understand what the problem is, I suggest you to study the question or ask questions rather than try to make a point.

You can't be as smart as you think or you would know that "proof by authority" is a fallacy.
July 01, 2013
On Monday, 1 July 2013 at 09:31:04 UTC, JS wrote:
> On Monday, 1 July 2013 at 06:51:53 UTC, deadalnix wrote:
>> On Monday, 1 July 2013 at 06:38:20 UTC, JS wrote:
>>> well duh, but it is quite a simple mathematical problem and your counter-example is not one at all.
>>>
>>> For a statically typed language all types must be known at compile time... so you can't come up with any valid counter-example. Just because you come up with some convoluted example that seems to break the algorithm does not prove anything.
>>>
>>> Do you agree that a function's return type must be known at compile time in a statically typed language? If not then we have nothing more to discuss... (Just because you allow a function to be compile time polymorphic doesn't change anything because each type that a function can possibly return must be known)
>>
>> As a compiler implementer, Timon is probably way more competent than you are on the question. You'll get anything interesting to add by considering you know better.
>>
>> The type of problem he mention are already present in many aspect of D and makes it really hard to compile in a consistent way accross implementations. Adding new one is a really bad idea.
>>
>> If you don't understand what the problem is, I suggest you to study the question or ask questions rather than try to make a point.
>
> You can't be as smart as you think or you would know that "proof by authority" is a fallacy.

Authority is not proof, but many years of experience provide a perspective that is worth serious consideration. Which is what deadalnix said.
July 01, 2013
On 6/30/13 10:30 PM, JS wrote:
> On Monday, 1 July 2013 at 01:08:49 UTC, Kenji Hara wrote:
>> 2013/7/1 JS <js.mdnq@gmail.com>
>>
>>> I am simply talking about having the compiler enlarge the type if
>>> needed.
>>> (this is mainly for built in types since the type hierarchy is
>>> explicitly
>>> known)
>>>
>>
>> Just a simple matter, it would *drastically* increase compilation time.
>>
>> void foo()
>> {
>>     auto elem;
>>     auto arr = [elem];
>>
>>     elem = 1;
>>     ....
>>     elem = 2.0;
>>     // typeof(elem) change should modify the result of typeof(arr)
>> }
>>
>> Such type dependencies between multiple variables are common in the
>> realistic program.
>>
>> When `elem = 2.0;` is found, compiler should run semantic analysis of the
>> whole function body of foo _once again_, because the setting type of elem
>> ignites the change of typeof(arr), and it would affect the code meaning.
>>
>> If another variable type would be modified, it also ignites the whole
>> function body semantic again.
>>
>> After all, semantic analysis repetition would drastically increase.
>>
>> I can easily imagine that the compilation cost would not be worth the
>> small
>> benefits.
>>
>> Kenji Hara
>
> No, this would be a brute force approach. Only one "preprocessing pass"
> of (#lines)  would be required. Since parsing statement by statement
> already takes place, it should be an insignificant cost.

Believe me, it's not. Look at this:

---
int foo(int elem) {
  return 1;
}

char foo(float elem) {
  return 'a';
}

auto elem;
elem = 1;
auto other = foo(elem);
elem = other + 2.5;
---

Explain to me how the compiler would work in this case, step by step.

July 01, 2013
On 6/30/13 7:39 PM, JS wrote:
> On Saturday, 29 June 2013 at 19:18:13 UTC, Ary Borenszweig wrote:
>> On 6/27/13 9:34 PM, JS wrote:
>>> Would it be possible for a language(specifically d) to have the ability
>>> to automatically type a variable by looking at its use cases without
>>> adding too much complexity? It seems to me that most compilers already
>>> can infer type mismatchs which would allow them to handle stuff like:
>>>
>>> main()
>>> {
>>>    auto x;
>>>    auto y;
>>>    x = 3;   // x is an int, same as auto x = 3;
>>>    y = f(); // y is the same type as what f() returns
>>>    x = 3.9; // x is really a float, no mismatch with previous type(int)
>>> }
>>>
>>> in this case x and y's type is inferred from future use. The compiler
>>> essentially just lazily infers the variable type. Obviously ambiguity
>>> will generate an error.
>>
>> What you are asking is essentially what Crystal does for all variables
>> (and types):
>>
>> https://github.com/manastech/crystal/wiki/Introduction#type-inference
>>
>> Your example would be written like this:
>>
>> x = 3
>> y = f()
>> x = 3.9
>>
>> But since Crystal transforms your code to SSA
>> (http://en.wikipedia.org/wiki/Static_single_assignment_form) you
>> actually have *two* "x" variables in your code. The first one is of
>> type Int32, the second of type Float64. The above solves the problem
>> mentioned by Steven Schveighoffer, where you didn't know what
>> overloaded version you was calling:
>>
>> x = 3
>> f(x) # always calls f(Int32), because at run-time
>>      # x will always be an Int32 at this point
>> x = 3.9
>>
>> But to have this in a language you need some things:
>>
>> 1. Don't have a different syntax for declaring and updating variables
>> 2. Transform your code to SSA
>> (maybe more?)
>>
>> So this is not possible in D right now, and I don't think it will ever
>> be because it requires a huge change to the whole language.
>
> This is not what I am talking about and it seems quite dangerous to have
> one variable name masquerade as multiple variables.

Why dangerous? I've been programming in Ruby for quite a time and never found it to be a problem, but an advantage. Now I'm programming in Crystal and it's the same, but the compiler can catch some errors too.

Show me an example where this is dangerous (the pointer example gave by Walter is not valid anymore since it has a fix).

July 01, 2013
On 6/30/13 10:56 PM, Timon Gehr wrote:
> On 07/01/2013 03:08 AM, Kenji Hara wrote:
>> 2013/7/1 JS <js.mdnq@gmail.com <mailto:js.mdnq@gmail.com>>
>>
>>     I am simply talking about having the compiler enlarge the type if
>>     needed. (this is mainly for built in types since the type hierarchy
>>     is explicitly known)
>>
>>
>> Just a simple matter, it would *drastically* increase compilation time.
>>
>> void foo()
>> {
>>      auto elem;
>>      auto arr = [elem];
>>
>>      elem = 1;
>>      ....
>>      elem = 2.0;
>>      // typeof(elem) change should modify the result of typeof(arr)
>> }
>>
>> Such type dependencies between multiple variables are common in the
>> realistic program.
>>
>> When `elem = 2.0;` is found, compiler should run semantic analysis of
>> the whole function body of foo _once again_, because the setting type of
>> elem ignites the change of typeof(arr), and it would affect the code
>> meaning.
>>
>> If another variable type would be modified, it also ignites the whole
>> function body semantic again.
>>
>> After all, semantic analysis repetition would drastically increase.
>>
>> I can easily imagine that the compilation cost would not be worth the
>> small benefits.
>>
>> Kenji Hara
>
> The described strategy can easily result in non-termination, and which
> template instantiations it performs can be non-obvious.
>
> auto foo(T)(T arg){
>      static if(is(T==int)) return 1.0;
>      else return 1;
> }
>
> void main(){
>      auto x;
>      x = 1;
>      x = foo(x);
> }

Just tried it in Crystal and it ends alright. It works like this:

1. x is an Int
2. you call foo(x), it returns a float so x is now a float (right now in Crystal that's a union of int and float, but that will soon change).
3. Since x is a float, foo returns an int, but assigning it to x, which is already a float, gives back a float.
4. No type changed, so we end.

Crystal also supports recursive and mutuilly recursive functions. The compiler is always guaranteed to finish.

(I'm just using Crystal as an example to have a proof that it can be done)
July 01, 2013
On 07/01/2013 03:44 PM, Ary Borenszweig wrote:
> On 6/30/13 10:56 PM, Timon Gehr wrote:
>> On 07/01/2013 03:08 AM, Kenji Hara wrote:
>>> 2013/7/1 JS <js.mdnq@gmail.com <mailto:js.mdnq@gmail.com>>
>>>
>>>     I am simply talking about having the compiler enlarge the type if
>>>     needed. (this is mainly for built in types since the type hierarchy
>>>     is explicitly known)
>>>
>>>
>>> Just a simple matter, it would *drastically* increase compilation time.
>>>
>>> void foo()
>>> {
>>>      auto elem;
>>>      auto arr = [elem];
>>>
>>>      elem = 1;
>>>      ....
>>>      elem = 2.0;
>>>      // typeof(elem) change should modify the result of typeof(arr)
>>> }
>>>
>>> Such type dependencies between multiple variables are common in the
>>> realistic program.
>>>
>>> When `elem = 2.0;` is found, compiler should run semantic analysis of
>>> the whole function body of foo _once again_, because the setting type of
>>> elem ignites the change of typeof(arr), and it would affect the code
>>> meaning.
>>>
>>> If another variable type would be modified, it also ignites the whole
>>> function body semantic again.
>>>
>>> After all, semantic analysis repetition would drastically increase.
>>>
>>> I can easily imagine that the compilation cost would not be worth the
>>> small benefits.
>>>
>>> Kenji Hara
>>
>> The described strategy can easily result in non-termination, and which
>> template instantiations it performs can be non-obvious.
>>
>> auto foo(T)(T arg){
>>      static if(is(T==int)) return 1.0;
>>      else return 1;
>> }
>>
>> void main(){
>>      auto x;
>>      x = 1;
>>      x = foo(x);
>> }
>
> Just tried it in Crystal

Using overloaded functions, I guess? It is not really the same thing, because those need to be type checked in any case.

> and it ends alright.

(Note that I was specifically addressing the method Kenji Hara lined out, which appears to completely restart type checking every time a type changes.)

> It works like this:
>
> 1. x is an Int
> 2. you call foo(x), it returns a float so x is now a float (right now in
> Crystal that's a union of int and float, but that will soon change).
> 3. Since x is a float, foo returns an int, but assigning it to x, which
> is already a float, gives back a float.
> 4. No type changed, so we end.
>...

This kind of fixed-point iteration will terminate in D in most relevant cases (it is possible to create an infinitely ascending chain of types, but then, type checking failing implicit conversions won't terminate anyway).

But note that now x is a double even though it is only assigned ints.

Furthermore, this approach still implicitly instantiates template versions that are not referred to in the final type checked code.

July 01, 2013
On 7/1/2013 6:39 AM, Ary Borenszweig wrote:
>> This is not what I am talking about and it seems quite dangerous to have
>> one variable name masquerade as multiple variables.
>
> Why dangerous?

D already disallows:

    int x;
    {   float x;
    }

as an error-prone construct, so why should it allow:

    int x;
    float x;

?

> I've been programming in Ruby for quite a time and never found it
> to be a problem, but an advantage.

What advantage? Does Ruby have a shortage of names for variables? (Early versions of BASIC only allowed variable names with one letter, leading to some pretty awful workarounds.)


> Show me an example where this is dangerous (the pointer example gave by Walter
> is not valid anymore since it has a fix).

I'm actually rather sure that one can come up with rule after rule to 'fix' each issue, but good luck with trying to fit all those rules into some sort of comprehensible framework. Consider what happened to C++ when they tried to fit new things into the overloading rules - the rules now span multiple pages of pretty arbitrary rules, and practically nobody understands the whole. What people do is randomly try things until it seems to do the right thing for them, and then they move on.

And all this for what - nobody has come up with a significant reason to support this proposal. I see post after post in this thread trying to make it work, and nothing about what problem it solves.

July 01, 2013
On 7/1/13 1:15 PM, Walter Bright wrote:
> On 7/1/2013 6:39 AM, Ary Borenszweig wrote:
>>> This is not what I am talking about and it seems quite dangerous to have
>>> one variable name masquerade as multiple variables.
>>
>> Why dangerous?
>
> D already disallows:
>
>      int x;
>      {   float x;
>      }
>
> as an error-prone construct, so why should it allow:
>
>      int x;
>      float x;
>
> ?

Well, those constructs don't even make sense because in the examples I gave I never say what type I want my variables to be. I let the compiler figure it out.

>
>> I've been programming in Ruby for quite a time and never found it
>> to be a problem, but an advantage.
>
> What advantage? Does Ruby have a shortage of names for variables? (Early
> versions of BASIC only allowed variable names with one letter, leading
> to some pretty awful workarounds.)

I'll give you an example:

# var can be an Int or String
def foo(var)
  var = var.to_s
  # do something with var, which is now guaranteed to be a string
end

I can call it like this:

foo(1)
foo("hello")

If I had to put types, I would end up doing of of these:

1.

void foo(int var) {
  foo(to!string(var))
}

void foo(string var) {
  // do something with var
}

2.

void foo(T)(T var) {
  string myVar;
  static if (is(T == string)) {
    myVar = var;
  } else if (is(T == int)) {
    myVar = to!string(var)
  } else {
    static assert(false);
  }
  // do something with myVar
}

Both examples are ugly and verbose (or, at least, the example in Ruby/Crystal is much shorter and cleaner). The example I give is very simple, I can reuse a var which *has the same meaning* for me when I'm coding and I don't need to come up with a new name.

It's not that Ruby has a shortage of names. It's just that I don't want to spend time thinking new, similar names, just to satisfy the compiler.

(And if you are worried about efficiency, the method "#to_s" of String just returns itself, so in the end it compiles to the same code you could have written manually like I showed in D)

>
>
>> Show me an example where this is dangerous (the pointer example gave
>> by Walter
>> is not valid anymore since it has a fix).
>
> I'm actually rather sure that one can come up with rule after rule to
> 'fix' each issue, but good luck with trying to fit all those rules into
> some sort of comprehensible framework. Consider what happened to C++
> when they tried to fit new things into the overloading rules - the rules
> now span multiple pages of pretty arbitrary rules, and practically
> nobody understands the whole. What people do is randomly try things
> until it seems to do the right thing for them, and then they move on.
>
> And all this for what - nobody has come up with a significant reason to
> support this proposal. I see post after post in this thread trying to
> make it work, and nothing about what problem it solves.

Exactly, because in D you need to specify the types of variables. And that goes against inferring a variable's type from its usage (which is different from inferring it from its initializer).

I'm also against this proposal. I'm just saying that in D it's not feasible, and if you want to make it work you'll have to change so many things that you'll end up with a different language.