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

Allow modifications to local variables in pure functions #1422

Closed
kud1ing opened this issue Jan 4, 2012 · 17 comments
Closed

Allow modifications to local variables in pure functions #1422

kud1ing opened this issue Jan 4, 2012 · 17 comments
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one.

Comments

@kud1ing
Copy link

kud1ing commented Jan 4, 2012

I know this issue is on someone's plate, but i could not find a related issue.

Currently we can not iterate over strings, vectors, lists etc using pure functions. That means that functions like str::is_whitespace() can not be declared pure either, even though logically they are.

@marijnh
Copy link
Contributor

marijnh commented Jan 4, 2012

You'd still not be able to make iter pure, since it calls one of its arguments and can't prove that this argument is pure.

You can add more rules to handle that, but it just goes down deeper and deeper. As we found out before (there was an effect system early last year that was dumped again), you can't really do any kind of sane purity checking on a language like Rust. I would prefer going back to a system where you can simply annotate a function that you want to use as a predicate as pure (or, preferably, pred), and give up on any checking of this purity.

@kud1ing
Copy link
Author

kud1ing commented Jan 4, 2012

It would not be possible to add the "pure" declaration to a higher-order function's arguments? Like

pure fn any<T>(v: [T], f: pure fn(T) -> bool) -> bool

@marijnh
Copy link
Contributor

marijnh commented Jan 4, 2012

We could make purity part of a function's type. But that still doesn't come close to solving the problem. Consider this:

fn count_zeroes(x: [int]) -> uint {
    let cnt = 0u;
    vec::iter(x, {|elt| if (elt == 0) { cnt += 1u; }});
    ret cnt;
}

Is this pure? The block passed to iter sure isn't. (And sure, you can come up with more and more rules to cover more and more cases, but it'll get really complicated and hard to understand. Also, actually annotating all pure functions, not just those that are used as predicates, is very much like "const correctness" in C++ -- people will forget or not bother, and as a result the whole system, which requires consistency, falls apart.)

@kud1ing
Copy link
Author

kud1ing commented Jan 4, 2012

My naive defininition of "pure" is: "a pure function can do whatever it wants, unless that impureness does not escape its scope".

  • The block {|elt| if (elt == 0) { cnt += 1u; }} is not pure, since it affects state outside of its scope.
  • "count_zeroes()" would still be pure, because its impure implentation does not escape its scope.

Does that make sense?

Or simpler:

pure fn a_number() -> uint {
    let cnt = 0u;
    cnt += 42u;
    ret cnt;
}

Would use state-manipulation internally, but it would not escape.

@marijnh
Copy link
Contributor

marijnh commented Jan 4, 2012

Sure that makes sense. Formalizing that sensible definition is where the problem lies.

@kud1ing
Copy link
Author

kud1ing commented Jan 4, 2012

There seemed to be a paper “Grafting Functional Support on Top of an Imperative Language” by Andrei Alexandrescu on D's implementation of purity, but i can not find it.

Their definition of "pure" can be found here: http://www.d-programming-language.org/function.html

@catamorphism
Copy link
Contributor

Marijn -- I'm confused about this comment "I would prefer going back to a system where you can simply annotate a function that you want to use as a predicate as pure (or, preferably, pred), and give up on any checking of this purity." This is exactly what unchecked blocks do, which I added back in August or September. (I can't remember what the exact syntax we decided on was.) Or did those go away?

@marijnh
Copy link
Contributor

marijnh commented Jan 6, 2012

We have unchecked blocks, but we also have an attempt at formal purity checking. I'm proposing we get rid of the second, since it's just annoying and broken, and just implicitly not check purity, just believing the programmer whenever he/she annotates a function as pure.

@catamorphism
Copy link
Contributor

I feel like we already discussed the "get rid of purity checking" possibility back in the summer when we were discussing these issues thoroughly -- has anything changed since then?

@marijnh
Copy link
Contributor

marijnh commented Jan 6, 2012

What was the conclusion of that discussion?

@catamorphism
Copy link
Contributor

The conclusion was that it's important to give the programmer the freedom to choose to write their typestate predicates a restricted sublanguage that is statically guaranteed to be pure, because otherwise they're not getting strong guarantees from the compiler. At the same time, they also have the freedom to use unchecked blocks to say "trust me"; leaving purity checking in doesn't take away freedom from someone who only wants to use the "unchecked block" option. They can just throw an unchecked block around all predicates.

@ssylvan
Copy link

ssylvan commented Jan 22, 2012

Would something like Haskell's ST monad help? Not necessarily explicitly using a monad of course (indeed, it would be ideal if you didn't even know this was going on under the covers!).

The general idea is that each mutable value would be tagged with a specific type variable (one that's "fresh" for the given "pure" function), and every time you do something with mutable variable you just thread this type variable through everything (making sure they match). Then at the end you strip it out in the "runST" call. Since the result value's type doesn't have access to this type variable, it couldn't posssibly contain any mutable references leaking side effects.

Maybe typestate could be used to do the same kind of tracking (with modifications). They key point is that anything impure is associated with a fresh "tag" introduced by the "pure" annotation, and that you ensure that nothing tagged with this "tag" can escape the function, statically.

BTW, this not only means you can modify local variables on the stack, you can modify local heap allocations too.

@ghost ghost assigned catamorphism Apr 12, 2012
@catamorphism
Copy link
Contributor

Assigning this to myself, but I don't think it's wise to spend time on it until we reach a clearer consensus about what we want in the long term w.r.t typestate and purity checking (if anything) -- see #2178.

@nikomatsakis
Copy link
Contributor

as part of the borrowing check that I am working on, I have all the capabilities to solve this in a nice way (as I already have to know which data is interior to the stack frame and what is not).

@ssylvan
Copy link

ssylvan commented May 5, 2012

It would be interesting to add some kind of warning about a function that's really pure not being marked as such. Then you can measure how many functions are really pure in a code base. If the pure to impure ratio on some real programs is more than, say, 50% (or even lower), maybe the modifier should be inverted so that pure is the default.

@nikomatsakis
Copy link
Contributor

you can now modify local state (that is, state which is uniquely tied to the current stack frame) in pure fns. there is still the question of allowing pure fns to invoke closures and so forth---I think this is best addressed by an adapted version of the Lighweight Polymorphic Effects functions, which basically allow a function to be "as pure as its argument closures are"---but that seems like a separate issue.

@kud1ing
Copy link
Author

kud1ing commented May 12, 2012

Great, thanks.

celinval pushed a commit to celinval/rust-dev that referenced this issue Jun 4, 2024
…1447)

* Changed copyright check to the new standard PR 1422.

* Fixed debug print.

* Added doc comment.

* Changed to proper doc-comment

* reverted test cases

* Changed generator test cases to new format.

* Added check that above our license is empty or comments

* Fixed merge fail

* Escaped backslash.

* Allowed empty 2nd line pattern discussed in rust-lang#1422

* Test case for maybe empty lines above.

* Changed from list to generator.
celinval added a commit to celinval/rust-dev that referenced this issue Jun 4, 2024
Co-authored-by: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com>
jieyouxu added a commit to jieyouxu/rust that referenced this issue Feb 28, 2025
…obzol

Bump `rustc_{codegen_ssa,llvm}` `cc` to 1.2.16 to fix `x86` Windows jobs on newest Windows SDK

Part of rust-lang#137733.

Bump `rustc_{codegen_ssa,llvm}` `cc` to 1.2.16 which contains rust-lang/cc-rs#1425 to help with rust-lang#137733. Previously tested in rust-lang#137724.

#### `cc` changelog between 1.2.13 and 1.2.16

<details>
<summary>`cc` changes since 1.2.13 up to and including 1.2.16</summary>

##### [1.2.16](rust-lang/cc-rs@cc-v1.2.15...cc-v1.2.16) - 2025-02-28

###### Fixed

- force windows compiler to run in `out_dir` to prevent artifacts in cwd (rust-lang#1415)

###### Other

- use `/arch:SSE2` for `x86` target arch (rust-lang#1425)
- Regenerate windows-sys binding ([rust-lang#1422](rust-lang/cc-rs#1422))
- Regenerate target info ([rust-lang#1418](rust-lang/cc-rs#1418))
- Add LIB var when compiling flag_check (rust-lang#1417)
- Change flag ordering ([rust-lang#1403](rust-lang/cc-rs#1403))
- Fix archiver detection for musl cross compilation ([rust-lang#1404](rust-lang/cc-rs#1404))

##### [1.2.15](rust-lang/cc-rs@cc-v1.2.14...cc-v1.2.15) - 2025-02-21

###### Other

- Regenerate target info ([rust-lang#1406](rust-lang/cc-rs#1406))
- Always read from all `CFLAGS`-style flags ([rust-lang#1401](rust-lang/cc-rs#1401))
- Simplify the error output on failed `Command` invocation ([rust-lang#1397](rust-lang/cc-rs#1397))

##### [1.2.14](rust-lang/cc-rs@cc-v1.2.13...cc-v1.2.14) - 2025-02-14

###### Other

- Regenerate target info ([rust-lang#1398](rust-lang/cc-rs#1398))
- Add support for setting `-gdwarf-{version}` based on RUSTFLAGS ([rust-lang#1395](rust-lang/cc-rs#1395))
- Add support for alternative network stack io-sock on QNX 7.1 aarch64 and x86_64 ([rust-lang#1312](rust-lang/cc-rs#1312))

</details>

r? `@Kobzol`
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Feb 28, 2025
Rollup merge of rust-lang#137788 - jieyouxu:bump-compiler-cc, r=lqd,Kobzol

Bump `rustc_{codegen_ssa,llvm}` `cc` to 1.2.16 to fix `x86` Windows jobs on newest Windows SDK

Part of rust-lang#137733.

Bump `rustc_{codegen_ssa,llvm}` `cc` to 1.2.16 which contains rust-lang/cc-rs#1425 to help with rust-lang#137733. Previously tested in rust-lang#137724.

#### `cc` changelog between 1.2.13 and 1.2.16

<details>
<summary>`cc` changes since 1.2.13 up to and including 1.2.16</summary>

##### [1.2.16](rust-lang/cc-rs@cc-v1.2.15...cc-v1.2.16) - 2025-02-28

###### Fixed

- force windows compiler to run in `out_dir` to prevent artifacts in cwd (rust-lang#1415)

###### Other

- use `/arch:SSE2` for `x86` target arch (rust-lang#1425)
- Regenerate windows-sys binding ([rust-lang#1422](rust-lang/cc-rs#1422))
- Regenerate target info ([rust-lang#1418](rust-lang/cc-rs#1418))
- Add LIB var when compiling flag_check (rust-lang#1417)
- Change flag ordering ([rust-lang#1403](rust-lang/cc-rs#1403))
- Fix archiver detection for musl cross compilation ([rust-lang#1404](rust-lang/cc-rs#1404))

##### [1.2.15](rust-lang/cc-rs@cc-v1.2.14...cc-v1.2.15) - 2025-02-21

###### Other

- Regenerate target info ([rust-lang#1406](rust-lang/cc-rs#1406))
- Always read from all `CFLAGS`-style flags ([rust-lang#1401](rust-lang/cc-rs#1401))
- Simplify the error output on failed `Command` invocation ([rust-lang#1397](rust-lang/cc-rs#1397))

##### [1.2.14](rust-lang/cc-rs@cc-v1.2.13...cc-v1.2.14) - 2025-02-14

###### Other

- Regenerate target info ([rust-lang#1398](rust-lang/cc-rs#1398))
- Add support for setting `-gdwarf-{version}` based on RUSTFLAGS ([rust-lang#1395](rust-lang/cc-rs#1395))
- Add support for alternative network stack io-sock on QNX 7.1 aarch64 and x86_64 ([rust-lang#1312](rust-lang/cc-rs#1312))

</details>

r? `@Kobzol`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one.
Projects
None yet
Development

No branches or pull requests

5 participants