C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Function overload set type information loss

From: Nathaniel Rupprecht <nathaniel.rupprecht_at_[hidden]>
Date: Wed, 31 Jul 2024 18:51:30 -0400
This is literally not true, the fact that two functions have the same type signature does not mean that they must be the same function. I don’t know why you think that templates need to have this magic property that absolutely no one thinks they need.

Just to be more mathematical, to hopefully make it very clear, just consider exactly the case you are considering here:

F: int -> int -> int
F x y = x y

Now create two separate functions by currying,
F1 = F 5 : int -> int
F2 = F -3 : int -> int

This is exactly what is going on here. This is very basic type theory. Both F1 and F2 have type int -> int. And they are also very clearly not the same function.

> On Jul 31, 2024, at 6:40 PM, organicoman <organicoman_at_[hidden]> wrote:
>
>
>
> Another thing, we keep hearing about how we need this proposal because “this isn’t how type theory works,” but this is not true, and is a misunderstanding of both how C++ implements types and of type theory itself.
> First off, if you want a really type-theory savvy language, you should probably be using Idris or Haskell or Coq, not C++. But the fundamental misunderstanding seems to be that a template instantiation is a dependently typed function.
>
> ```cpp
> template<typename T>
> int X(int) { /* ...*/ }
> ```
> It is not. The type of any specialization of this “function" is not
> * -> int -> int,
> because this is not a function at all, it is a template of how to create a function. For any specialization (e.g. X<int>, X<bool>, etc), the type of the resulting functions is just
> int -> int.
>
> This is called a mapping from int to int that means, if two functions stamped from the same template have the same mapping, then the same input gives the same output.
> But with current C++ it is not the case.
> Example:
> template<int I>
> int foo (int j)
> {
> return I * j;
> }
>
> int main()
> {
> auto f1 = &foo<5>;
> auto f2 = &foo<-3>;
>
> if(std::is_same_v<decltype(f1), decltype(f2)>)
> cout << " f1(1) must equal f2(1) is" << boolalpha<< (f1(1)==f2(1));
> }
>
> Try this snippet, and reason about it. That's all what i can say.
>
> Same with similar functions, even something like
> ```cpp
> template<typename T>
> Int X(T y) { /* … */ }
> ```
> Things like X<int> and X<double> have type int -> int and double -> int, they are “curried by the compiler,” if you want. So yes, this
>
> int main()
> {
> auto f_ptr1= &foo<int, double>;
> auto f_ptr2= &foo<char, double>;
> static_assert(std::is_same_v<decltype(f_ptr1), decltype(f_ptr2)>); // assertion passed.
> }
>
> is completely correct. Because foo<int, double> and foo<char, double> both have type () -> (), because they are both functions that take no arguments and return no value. It does not matter how you constructed the function. How you constructed a function is not its type signature. That is just not mathematically true, and it is certainly not true in C++ either.
>
>
> You are asking for something that does not exist in type theory, you are asking for something like this
>
> F : X -> Y -> Z, a : X
> G = F a
>
> to somehow magically result in G not having type Y -> Z (which it clearly, type theoretically does), but have some magical new type
> <X> Y -> Z
> where the function G “remembers” the “history” of how it was “created” in virtue of it being equal to the currying of another function.
>
> This isn’t a thing.
>
>> On Jul 31, 2024, at 5:23 PM, organicoman via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>
>>
>>
>>
>> Problem is that you try to break C++ type system.
>>
>> You suggest:
>> ```
>> "PFvvE_d" == typeid(f<double>).name()
>> ```
>>
>> And this same by analogy:
>>
>> ```
>> "i_42" == typeid(42).name()
>> ```
>>
>> Nope that's a plain prvalue int. There is no type information lost in the value 42.
>>
>> Why only template parameters are propagated, not function `f` itself too?
>> I even recall that Rust does something like this, each function has
>> its own type that is
>> inheriting from type based on this function signature
>> (thanks this you can still have function pointers).
>>
>> Besides your suggestion that `auto x` can propagate more than normal
>> type and value
>> is DoA as it would be breaking change, consider this simple code:
>>
>> ```
>> template<typename T>
>> void foo() { }
>>
>> void foo2() { }
>>
>> int bar(auto f)
>> {
>> static int i = 0;
>> return ++i;
>> }
>>
>> int main()
>> {
>> bar(&foo2);
>> bar(&foo<int>);
>> bar(&foo<double>);
>> bar(&foo<float>);
>> return bar(&foo<long>);
>> }
>> ```
>>
>> It needs to return `5`. And because of this you can't do any magic in
>> `bar` to extract additional
>>
>> It will print 5, there is no type information manipulated inside bar, thus the compiler will stamp the usual code.
>>
>> information from `f` argument even if the compiler knows it as it
>> would be an ODR violation.
>>
>> The effective type will be used only when the user specify it inside a block scope.
>> That way the compiler is notified to track the real type provided by the user, and process the information discrad it after if it proves necessary.
>> Type manipulation is the compiler business.
>> Value manipulation is the runtime business.
>> >
>> > On Wed, Jul 31, 2024 at 6:21 AM organicoman via Std-Proposals <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>> wrote:
>> >>
>> >>
>> >>
>> >>
>> >>
>> >> Sent from my Galaxy
>> >>
>> >>
>> >>
>> >> You have been the one who wanted to different types with decltype and effective_decltype.
>> >>
>> >>
>> >>
>> >> Why are you complaining?
>> >>
>> >> The correct answer in your example must be in the following form:
>> >>
>> >> // one plausible mangled name
>> >>
>> >> PFvvE_i <--- f<int>
>> >>
>> >> PFvvE_d <--- f<double>
>> >>
>> >> the radix PFvvE is the same denoting the same apparent type, and different tags { _i, _d }, to denote the effective type.
>> >>
>> >> Likewise, the type information is not lost. And you can trace back to what you have called.
>> >>
>> >>
>> >> Anyway, I think we touched everything in this thread, and thanks to you and Tiago i found a corner case, but solved it.
>> >>
>> >> I think it is time to compile some wording and post it here, maybe it will be more clear.
>> >>
>> >>
>> >> -----Ursprüngliche Nachricht-----
>> >> Von: organicoman <organicoman_at_[hidden] <mailto:organicoman_at_[hidden]>>
>> >> Gesendet: Mi 31.07.2024 10:50
>> >> Betreff: Re: [std-proposals] Function overload set type information loss
>> >> An: Sebastian Wittmeier via Std-Proposals <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>>;
>> >> CC: Sebastian Wittmeier <wittmeier_at_[hidden] <mailto:wittmeier_at_[hidden]>>;
>> >>
>> >>
>> >>
>> >> With function pointers you can achieve something like; in most cases even this is only possible at runtime:
>> >>
>> >>
>> >>
>> >>
>> >>
>> >> PFvvE <--| do you see the problem here
>> >> PFvvE <--| f<int>,f<double>, yet the same name
>> >> Just one function f.
>> >> Just one function f.
>> >> f<double>
>> >> f<int>
>> >>
>> >> In "type theory" that's not correct.
>> >> Two entity of different signature must have
>> >> different types. Otherwise a many to one
>> >> relationship takes place.
>> >>
>> >> --
>> >> Std-Proposals mailing list
>> >> Std-Proposals_at_[hidden]
>> >> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>> >
>> > --
>> > Std-Proposals mailing list
>> > Std-Proposals_at_[hidden]
>> > https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals


Received on 2024-07-31 22:51:45