-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Pure functions/effect system #1631
Comments
Would |
@burdges Taking a reference to |
I believe the general solution for this which is not completely cumbersome, and can support various predicates on the behavior (another useful one is "can panic/abort") is an effect system, but I can't find open issues on anything like that. |
@burdges I guess |
I guess |
I'd agree with @eddyb that one should work out how a (non-)effects system might fit into Rust. A priori, I'd wager this requires first working out how higher-kinded types fit in, if only because effects are monads in Idris. Ain't clear if a non-effect like There might be interesting papers on using HTKs to do non-effects like |
Did anything change since http://thread.gmane.org/gmane.comp.lang.rust.devel/3674/focus=3855 ? |
Rust's affine types would in fact already enable having a really strong and flexible notion of purity that's well-integrated with the existing type system, without having to add any major new features or annotations on top of it. If you chase links from this recent discussion I had on reddit you can read more of my thoughts about it. TL;DR there'd be a zero-sized But it's not something that could be easily retrofitted onto Rust 1.x. |
I am against this because of the lack of generality. Either way, such an addition should be general and most importantly expressive, covering lots of possible use cases (an example is the Finally, I could see such a feature being abused in one way or another, especially since it might not be consistent across libraries. One way that could be solved is to provide a medium sized collection of common "effect systems" (in the lack of a better term), but uniformity is still not guaranteed: each library might have its own definition of "secure" and "not secure", and these might or might not play well together. |
|
Design by contract may be a possible path for this by allowing a contract to flag all side effects a function may generate. With Spark14 (maybe Ada12 as well), you specify which globals/statics you may read from, which you may read & write, and which parameters you may write. Params passed in are assumed read access. Then would need the compiler to propagate those contracts and enforce that if B calls A, then B's contract must contain the same level of side effects as A in it's contract. For backwards compatibility, no listed contracts would have to be assumed as "anything goes". I would not agree with allowing |
I think "unsafe pure" is "hey compiler, you can't check the purity of this but I'm a smart developer so trust me on this". Allowing some RPCs to be pure would be nice, and "unsafe pure" does exactly that. In theory you'd need to use The compiler can't guarantee a RPC is pure because 1. it knows nothing about RPCs 2. it's implemented over I/O which's impure, but that doesn't mean a RPC isn't pure. Take, for example, getting the title of a Guake tab. This must be implemented as a RPC, but it's still pure as it mutates nothing. |
Huh, I may be crazy, but: How much of the |
@killercup I think there are quite a lot of limitations in the current RFC for |
That was my expectations as well. There are (unofficial) plans to use Miri Ilyas Gasanov [email protected] schrieb am Mo. 15. Aug. 2016 um
|
@SoniEx2 In my opinion, plain |
@glaebhoerl If traits can be implemented for Either that, or we could use Of course this implies that the types of a |
This has been much discussed in the past, and may even have existed in very old Rust. It is highly unlikely that we'd accept an RFC for this. The best way to make progress here (for anyone interested) would be to design and implement a system in a fork of the compiler and use that to prove utility. That would be huge, but very interesting task. |
@nrc Rust used to have an effect system ( |
More fine grained effect types were discussed, and proposed, but never agreed upon or implemented. I have no idea how we could retrofit it now :( |
Speaking of effect types in general, apart from side effects there is another useful concept - the one of predictable timing/performance. It would be nice to be able to guarantee that any call to some This is similar to certain goals that language subsets like MISRA C are designed to achieve. Common use cases for this are going to be interrupt and signal handlers, and generally whatever code constrained to real-time conditions. |
How do you guys feel about having effect system similar to Koka's? (see http://www.rise4fun.com/koka/tutorialcontent/guide or https://www.microsoft.com/en-us/research/wp-content/uploads/2016/08/algeff-tr-2016-v3.pdf). |
Adding purity to D language, despite several successive refinements, was a big failure. If you want to add something like that to Rust you need at least to do it in a much more principled way. Like row (set) effects: |
I wouldn't call pure functions in D "a big failure." Pure functions in D are very useful and interesting, and the rules for how they work are very logical. There are ways of implementing pure functions in programming languages, and you have to decide what it is you want to really implement. |
As |
Isn't |
@w0rp No, |
"Pure functions in D are very useful": what they are actually used for and useful for in D? (I've tried to use them for years and I've seen people try to use them for years). In D standard library even in the few spots where a purity annotation is useful, it's not used. |
@Centril Okay, that could be considered some definition of pure, then. @leonardo-m You don't need them, like how you don't need memory safety, but they are nice to have. I'm not sure I'd be able to convince you that they are useful, but I like them. I find them useful for documenting which functions may or may not have side effects, with compile time checking for that promise of no side effects. They are just difficult to use some times because too much code has side effects, even functions as simple as |
Things that I would consider pure (but not const):
Things that I would consider const (but not pure):
|
The more persuasive argument against a separate purity effect/annotation is that Rust already tried that before 1.0. I wasn't around back then, but I've heard that we ended up removing it because it wasn't that useful, no one could agree what it meant, and had composability issues that led to a de facto standard practice of marking everything as if it was impure. Unfortunately I can't find a source to cite for that right now. |
I guess we mostly have purity through immutable references already. Altho some ppl (diesel.rs >.>) don't use them correctly. |
I very much doubt that. This sounds more like there's a misunderstanding. Rust doesn't have immutable references. Rust only has shared references, and you can very much use shared references to mutate state. |
I'd expect diesel to take &mut Connections but (apparently?) it takes &Connections. I do consider "immutable" references to be a form of purity system. While you can use interior mutability for everything, which is like marking everything as |
I think that is a problematic semantics of purity and might be the cause of confusion in the future. The problem is, I can only find one real academic definiton of purity, which is in the realm of functional programming languages (lambda calculus) and I'm not sure how useful those semantics are: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.27.7800&rep=rep1&type=pdf Comparing with existing non-academic definitions of imperative languages:
The loosest definition is probably the gcc one (which is slightly different to const functions, because it allows reading global variables), but even that assumes no side-effects. |
What's wrong with the ability to have pure RPC functions? |
Because they are inherently side-effect full (possible network calls and whatnot, even if they are hidden behind an abstraction). I haven't seen a definition of "purity" yet that allows side-effects. I'm not convinced rust is a good experiment to introduce such a foggy definition. |
network state doesn't affect data state (unless you're Wireshark) |
Even if that was true, it's irrelevant to the definition of purity. Networking is a side-effect and introduces additional sources of failure that you cannot easily reason about. The function call may fail, regardless of input. How is that pure? You're introducing a totally new definition that no language (or academical source) has previously deployed. |
To be clear; it is not the failure itself that is the cause of impurity; Failure can easily be modelled in a partiality monad and can be handled purely. What is the cause of impurity is that if you call If you somehow manage to define a linear function |
Failure that doesn't depend on input (or initial program state) automatically violates referential transparency, no matter if you model it as a monadic value or not. So yes, I think we kind of mean the same. Expanding on that: Purity is most often defined in terms of evaluation. That means: if evaluation doesn't cause execution (e.g. of network calls), then the function is still pure. In practice, that means: if you make effects explicit via e.g. an IO type, then you can still have a pure function that does network stuff, given that your evaluation model doesn't cause execution of that IO action. That would allow the compiler to recognize this and have correct optimisation on pure functions in either way. Since we don't have an IO type (anymore) and evaluation and execution are not strictly separated, we cannot allow network calls. |
We are mostly in agreement, but I have a nit: This doesn't have to be the case. We can encode the following function: alwaysError :: b -> Maybe a
alwaysError _ = Nothing The use of I agree with your notes on practice, however, I think I showed that there can be pure networked functions given that they take an argument for which the value can never be repeated. I think this is a bizarre theoretical result which is boring and uninteresting, but well... EDIT: In this bizarre and unpractical world, |
You are right, my informal definition was maybe misleading. My point was rather that whether a function is pure or not doesn't depend on how you model failure (e.g. monadic). Which I think we still agree on.
Kind of, but it still meets the formal definition of purity, since it's defined as the "(weak) equivalence of call-by-name, call-by-value and call-by-need" (weak equivalence = considering undefined to be equivalent).
Whether a value can be repeated in practice is irrelevant for the theory of functional purity. A function doesn't suddenly turn pure or impure depending on what exact instance the value has in the range of the type. As such, every function that causes networking during evaluation is always impure. Do we agree on that? |
let's say you have a networked FFT function. that is, you call it with some set of values, and it calculates the FFT on a computing cluster. is it pure? yes, because it's an FFT. same input, same output, always. |
Absolutely not. The syscall may fail, your computer may be offline, the cluster may be down, corrupted, ... As such, calling this function twice may totally yield different results (including unexpected failure). |
oh yeah I can also point a heatgun to my RAM while running a pure local FFT function. those failures don't exist in the abstract machine. they're failures of the physical system. |
Execution is always impure. I'm not sure you listened to the conversation. |
networked FFT is pure, because if you consider the network as a whole as an abstract machine, it's no different from calling a local function. |
It isn't. That's not how you design a language. That's how you design your own architecture. You are confusing totally different abstraction levels. Please. |
the function's implementation should not affect its purity. neither should side-channels. (e.g. checking if your FFT is running locally or on another machine based on CPU usage) |
@SoniEx2 |
Using pure means the language has to guarantee or provide contracts that there would be no side-effects. Like @Serentty said if the language have no way to figure out the purity of function, its pointless to be able to mark a function as pure. |
I think this thread has outlived it's usefulness. |
Can we get
pure fn
which would be a way to indicate a function has no side-effects/can only call otherpure fn
s?It'd prohibit mutation of statics, pointers and references, and heap allocations. Traits would be able to demand
pure fn
s, which would be useful for getters.The text was updated successfully, but these errors were encountered: