Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] Is &$* a new cpp2 idiom? #247

Closed
filipsajdak opened this issue Jan 31, 2023 · 10 comments
Closed

[QUESTION] Is &$* a new cpp2 idiom? #247

filipsajdak opened this issue Jan 31, 2023 · 10 comments

Comments

@filipsajdak
Copy link
Contributor

filipsajdak commented Jan 31, 2023

As there is no possibility of passing a reference to the anonymous function (aka. lambda), you need to capture a variable as a pointer. That makes a new idiom emerge from the code: &$* - capture variable by pointer (and use as a reference).

The following code:

main: () -> int = {
    i := 42;

    l := :() = {
        std::cout << i&$* << std::endl;
    };

    l();
}

Will generate (skipping boilerplate):

[[nodiscard]] auto main() -> int{
    auto i {42}; 

    auto l {[_0 = (&i)](){                                    // capture by pointer
        std::cout << *cpp2::assert_not_null(_0) << std::endl; // dereference in place of use
    }}; 

    std::move(l)();
}

Or maybe I have missed something?


For all that wanders what i&$* means (please remember that cppfront uses postfix operators - https://github.com/hsutter/cppfront/wiki/Design-note%3A-Postfix-operators). I will add parentheses to explain:

i& - address of i,
(i&)$ - $ captures the value on the left (in that case, it is a pointer to i),
( (i&)$ )* - * dereference the value captured by $ (in that case, it is a pointer to i)

So, effectively i&$* is a reference to i that is captured by a lambda.

@filipsajdak
Copy link
Contributor Author

The funny thing is that it looks like grawlix :D

@AbhinavK00
Copy link

AbhinavK00 commented Jan 31, 2023

That makes me think, as there are no references in cpp2, how will we write functions that return a reference in cpp2? Like the [] operator for containers, or do we just start returning pointers in all cases?
Val also does not have references but they have projections which work by something called inversion of control, but I don't think those can be implemented at a transpiler level or if herb would even like to implement them.

@filipsajdak
Copy link
Contributor Author

@AbhinavK00, that is an excellent question. It is already covered by the forward return type passing style.

fun: (i : int) -> forward int = {
    return i;
}

fun2: (i) -> forward _ = {
    return i;
}

Generates (skipping boilerplate):

[[nodiscard]] auto fun(cpp2::in<int> i) -> int&{ // this is bug should be const int&
    return i; 
}

[[nodiscard]] auto fun2(auto const& i) -> decltype(auto){
    return i; 
}

@filipsajdak
Copy link
Contributor Author

filipsajdak commented Jan 31, 2023

cppfront syntax concentrates on intentions, not implementation details (reference here is an implementation detail). You can read more here: https://github.com/hsutter/708/blob/main/708.pdf (@hsutter paper about Parameter passing)

@AbhinavK00
Copy link

Thanks for the answer! Now we just need something similar for lambdas lol

@gregmarr
Copy link
Contributor

So with the current state of cppfront, i means "local variable from inside the function body", i$ means "capture by value" and i&$* means "capture by reference"? I'm not sure I like that.

I wonder, would it make sense to have i$ capture by reference, and then you can capture that value by adding another capture as i$$? That doesn't quite fit with the rest of the Capture design notes, where a copy is captured "Postconditions: [[post: v.size() == v.size()$ + 1]] -- capture a copy of v.size() on function entry for use later when the whole postcondition is evaluated on function exit".

"capture" means "take an expression in this context and store a copy of its current value for use later."
On the other hand, i isn't even in this context yet until you add the $. It could be that the intention was that everything in the outer scope is implicitly in the function scope, with capture by reference, and $ makes it capture a copy of the value, but that seems unsafe compared to current behavior, as it means that you are implicitly capturing things that may go out of scope before they may be used. Maybe there's an intention of having lifetime analysis done for anything captured by reference.

I guess that's enough speculation for me until @hsutter can clarify his thoughts here.

@filipsajdak
Copy link
Contributor Author

i&$* means "capture by reference"?

@gregmarr just to clarify. i&$* does not mean capture by reference. It captures a pointer (auto l {[_0 = (&i)](){ ) and dereferences it. It behaves like a reference in that context (std::cout << *cpp2::assert_not_null(_0) << std::endl;)

@gregmarr
Copy link
Contributor

@filipsajdak I understand, the effect is the same.

@filipsajdak
Copy link
Contributor Author

As we discuss references here, I think it is worth mentioning @hsutter sentence he said when I mentioned that std::optional could not hold references:

That's a feature, not a bug. :) It's deliberate that Cpp2 has no references.

More here: #106 (comment)

@hsutter
Copy link
Owner

hsutter commented Mar 1, 2023

@filipsajdak To answer your original question, yes that's the idiom. I do realize that &$* is an emergent idiom that comes from the design choice of "capture everything by value" + "in-place using $"... and I realize that it can look like line noise. (I had to look up "grawlix" :) ... that it looks like swearing could be a feature, rather than a bug, since it calls attention to what can be a dangerous construct...) I'm sitting back to see how it works out as I/we get experience using it in actual code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants