Re: purity question
On Tue, May 30, 2017 at 11:10:19AM -0700, Jonathan M Davis via Digitalmars-d-learn wrote: [...] > Yeah, basically, D's pure was originally what is now sometimes called > "strongly pure," which is quite close to functionally pure in that the > same input results in the same output (it still allows alocating > memory and returning it though, so you can have equal but different > objects if the same function is called multiple times with the same > arguments in different contexts where the compiler doesn't memoize > it). However, pure was so restrictive as to be borderline useless, > because it could only pass types that were immutable or implicitly > convertible to immutable. So, it was expanded to include any function > which could not access global, mutable state, because such functions > can be called from a "strongly" pure function without violating the > strongly pure function's guarantees - and thus "weakly" pure functions > were born, making it so that D's pure is really more like @noglobal > than functionally pure. It's a critical building block in functional > purity, and strongly pure functions still get all of the benefits that > they got before (and now they can actually be useful, because they can > call many more functions), but _most_ pure functions are weakly pure > and thus don't get the full benefits of functional purity - and that's > why some folks like Ketmar tend to get annoyed with D's pure. But the > way it is is actually quite useful even if it's initially confusing. > And even when there are no strongly pure functions in your program, > knowing that a function cannot access global variables except through > its arguments means a lot in and of itself. [...] In retrospect, we could have named "weakly pure" something like @noglobal, and "strongly pure" as pure. But be that as it may, I think D's pure system is actually extremely ingenious. Look at it this way: in the classical sense of functional purity, which is what you get in (pure) functional languages, it can get quite difficult to implement the functionality you want, because the language enforces that every primitive you use in your implementation must be pure. The reasoning is that if you're only allowed to use pure primitives, then the resulting function is guaranteed to be pure. It's nice and simple. However, it's also rather cumbersome, especially in the context of an imperative language like D. For example, in a functional language you cannot assign new values to variables, and you cannot write for-loops, because the loop index is not allowed to mutate. You cannot mutate anything, because mutation makes the code impure. So instead of straightforward loops, you need to resort to things like tail recursion; instead of mutation, you need to use monads, and so on. I'm not saying this is a bad thing, but it's just cumbersome because you, the programmer, cannot simply implement a function with a straightforward algorithm, but you have to work harder to express the algorithm in functional terms using recursion and other functional (pure) constructs. The first insight in D's purity system is the observation that, given some function f(), from the caller's POV all they care about is that (1) the function always returns the same value given the same arguments, and (2) there are no visible side-effects. Point (2) is where the insight lies: in a sense, it *doesn't matter* if f() does all kinds of impure stuff in order to compute its return value, as long as the outside world cannot see it. It may be internally impure, but externally, as far as the outside world is concerned, it's pure, because the caller can't tell the difference. So D allows things like variables, mutation, for-loops, and all kinds of stuff inside (strongly) pure functions -- as long as no global state is touched, and as long as the function arguments aren't modified (from the caller's POV), f() is pure. In other words, as long as f() keeps its dirty laundry to itself and leaves the outside world untouched, it is externally pure, even if it's internally impure. The next insight is this: suppose f() calls g(), and g() is impure. In the traditional functional language purity system, this is outright illegal, because a pure function, by definition, cannot call an impure function, otherwise it is itself impure. However, suppose g() does not modify any global state. It *may* modify stuff through its arguments -- which makes it impure. But if f() is not allowed to modify its arguments and has no global state, then at worst, it can only pass its internal, non-global state to g(). Therefore, it is impossible for g() to reach global state through its arguments. Thus, f()'s (external) purity is preserved. Note the fine distinction here, that g()'s external purity is keyed on being called from inside a strongly-pure function f(). If f() is impure, then there is no guarantee that g() may modify global state (because f() may pass a reference to a global
Re: purity question
On Tuesday, May 30, 2017 16:54:13 ag0aep6g via Digitalmars-d-learn wrote: > On 05/30/2017 11:12 AM, Rene Zwanenburg wrote: > > If malloc were marked as pure, wouldn't that mean it must return the > > same pointer every time you call it with the same size? > > D's `pure` mostly means: "does not access mutable state, and does not do > input/output". > > There is never a requirement that a function must return the same value > for the same input. But a compiler is allowed to memoize the result of a > `pure` function when it has no mutable indirections in its parameter and > return types. Such a function is "strongly pure". > > When there are mutable indirections, the function is "weakly pure". > Weakly pure functions are not assumed to be memoizable, but "weakly > pure" still has meaning: > > * Can call weakly pure functions from strongly pure ones. > > * When a weakly pure function has mutable indirections in the return > type but not in the parameters (like malloc), then it must be returning > freshly allocated memory. That means, the result cannot be referenced > from anywhere else. So it can be converted to const/immutable/shared > implicitly. The spec calls that a "pure factory function". > > Repeating Biotronic's links, the spec and David Nadlinger's article are > the go-to resources for D's take on purity: > > https://dlang.org/spec/function.html#pure-functions > http://klickverbot.at/blog/2012/05/purity-in-d/ Yeah, basically, D's pure was originally what is now sometimes called "strongly pure," which is quite close to functionally pure in that the same input results in the same output (it still allows alocating memory and returning it though, so you can have equal but different objects if the same function is called multiple times with the same arguments in different contexts where the compiler doesn't memoize it). However, pure was so restrictive as to be borderline useless, because it could only pass types that were immutable or implicitly convertible to immutable. So, it was expanded to include any function which could not access global, mutable state, because such functions can be called from a "strongly" pure function without violating the strongly pure function's guarantees - and thus "weakly" pure functions were born, making it so that D's pure is really more like @noglobal than functionally pure. It's a critical building block in functional purity, and strongly pure functions still get all of the benefits that they got before (and now they can actually be useful, because they can call many more functions), but _most_ pure functions are weakly pure and thus don't get the full benefits of functional purity - and that's why some folks like Ketmar tend to get annoyed with D's pure. But the way it is is actually quite useful even if it's initially confusing. And even when there are no strongly pure functions in your program, knowing that a function cannot access global variables except through its arguments means a lot in and of itself. - Jonathan M Davis
Re: purity question
On 05/30/2017 11:12 AM, Rene Zwanenburg wrote: If malloc were marked as pure, wouldn't that mean it must return the same pointer every time you call it with the same size? D's `pure` mostly means: "does not access mutable state, and does not do input/output". There is never a requirement that a function must return the same value for the same input. But a compiler is allowed to memoize the result of a `pure` function when it has no mutable indirections in its parameter and return types. Such a function is "strongly pure". When there are mutable indirections, the function is "weakly pure". Weakly pure functions are not assumed to be memoizable, but "weakly pure" still has meaning: * Can call weakly pure functions from strongly pure ones. * When a weakly pure function has mutable indirections in the return type but not in the parameters (like malloc), then it must be returning freshly allocated memory. That means, the result cannot be referenced from anywhere else. So it can be converted to const/immutable/shared implicitly. The spec calls that a "pure factory function". Repeating Biotronic's links, the spec and David Nadlinger's article are the go-to resources for D's take on purity: https://dlang.org/spec/function.html#pure-functions http://klickverbot.at/blog/2012/05/purity-in-d/
Re: purity question
On Tuesday, 30 May 2017 at 13:45:07 UTC, Rene Zwanenburg wrote: On Tuesday, 30 May 2017 at 11:34:52 UTC, ketmar wrote: If malloc were marked as pure, wouldn't that mean it must return the same pointer every time you call it with the same size? of course. but D "pure" is not what other world knows as "pure". we love to mess with words. Well, there's the ability to modify non-const reference parameters from a pure function, but that's not applicable to malloc. Are there any other special rules? The rules[0] are: 0) Can't call functions not marked pure. 1) Can't touch anything mutable that's not explicitly passed to the pure function. 1b) Except GC internal state - i.e. memory can be allocated via the GC. 1c) And floating-point exception flags and modes. 2) Can't do I/O (can be seen as a special case of 1 and 0). There's a few more details, but that's the important stuff. For a good article on the subject, I recommend David Nadlinger's Purity in D: http://klickverbot.at/blog/2012/05/purity-in-d/ [0]: https://dlang.org/spec/function.html#pure-functions
Re: purity question
Rene Zwanenburg wrote: On Tuesday, 30 May 2017 at 11:34:52 UTC, ketmar wrote: If malloc were marked as pure, wouldn't that mean it must return the same pointer every time you call it with the same size? of course. but D "pure" is not what other world knows as "pure". we love to mess with words. Well, there's the ability to modify non-const reference parameters from a pure function, but that's not applicable to malloc. Are there any other special rules? "pure" methods can mutate object state.
Re: purity question
On Tuesday, 30 May 2017 at 11:34:52 UTC, ketmar wrote: If malloc were marked as pure, wouldn't that mean it must return the same pointer every time you call it with the same size? of course. but D "pure" is not what other world knows as "pure". we love to mess with words. Well, there's the ability to modify non-const reference parameters from a pure function, but that's not applicable to malloc. Are there any other special rules?
Re: purity question
Rene Zwanenburg wrote: On Monday, 29 May 2017 at 01:36:24 UTC, Jonathan M Davis wrote: A simple example: anything that has a malloc/free pair. Yeah, if you do it right, you should be fine, but you have to do it right, and it's very easy to miss some detail that makes it wrong to insist to the compiler that what you're doing is pure. If malloc were marked as pure, wouldn't that mean it must return the same pointer every time you call it with the same size? of course. but D "pure" is not what other world knows as "pure". we love to mess with words.
Re: purity question
On Monday, 29 May 2017 at 01:36:24 UTC, Jonathan M Davis wrote: A simple example: anything that has a malloc/free pair. Yeah, if you do it right, you should be fine, but you have to do it right, and it's very easy to miss some detail that makes it wrong to insist to the compiler that what you're doing is pure. If malloc were marked as pure, wouldn't that mean it must return the same pointer every time you call it with the same size?
Re: purity question
On Monday, 29 May 2017 at 08:49:07 UTC, ketmar wrote: Brad Roberts wrote: libraries that themselves aren't marked pure, there's a real need for escape hatches. A simple example: anything that has a malloc/free pair. they aren't pure. it is a sad misconception about purity, which D makes even more complex by allowing to mark, for example, *setters* as pure. but still, `malloc()` and `free()` aren't pure. and while various functions in std.math, for example, are marked `pure`, they aren't too. There is pureMalloc since 2.074.0 (it was never announced): https://github.com/dlang/druntime/pull/1746 However, without a pureFree it's rather limited in usefulness and needs to be workaround in real life: https://github.com/dlang/druntime/pull/1718
Re: purity question
On Monday, 29 May 2017 at 11:25:06 UTC, ketmar wrote: almost all of them, 'cause they depends on FPU rounding settings. Well, yeah. IIRC contemporary floating point machine language instructions allow embedding of rounding mode into the instruction. A pity languages are lagging behind, stuck in the 1980s... WebAssembly is locking it to the default IEEE754-2008 round-to-even. Which is reasonable as they aim for max portability, albeit a bit limiting.
Re: purity question
Ola Fosheim Grøstad wrote: On Monday, 29 May 2017 at 08:49:07 UTC, ketmar wrote: pure. and while various functions in std.math, for example, are marked `pure`, they aren't too. Out of curiosity, which functions in std.math aren't "pure" in the D sense? almost all of them, 'cause they depends on FPU rounding settings.
Re: purity question
On Monday, 29 May 2017 at 08:49:07 UTC, ketmar wrote: pure. and while various functions in std.math, for example, are marked `pure`, they aren't too. Out of curiosity, which functions in std.math aren't "pure" in the D sense?
Re: purity question
Brad Roberts wrote: libraries that themselves aren't marked pure, there's a real need for escape hatches. A simple example: anything that has a malloc/free pair. they aren't pure. it is a sad misconception about purity, which D makes even more complex by allowing to mark, for example, *setters* as pure. but still, `malloc()` and `free()` aren't pure. and while various functions in std.math, for example, are marked `pure`, they aren't too.
Re: purity question
On Sunday, 28 May 2017 at 23:49:16 UTC, Brad Roberts wrote: Is there a mechanism for declaring something pure when it's built from parts which individually aren't? string foo(string s) { // do something arbitrarily complex with s that doesn't touch globals or change global state except possibly state of the heap or gc return s; } Ali has answered this two years ago: http://www.digitalmars.com/d/archives/digitalmars/D/learn/using_memset_withing_a_pure_function_74629.html#N74631 Copying for convenience: If you want to live dangerously, you can use assumePure, which is found in one of the unittest blocks of std.traits: import std.traits; auto assumePure(T)(T t) if (isFunctionPointer!T || isDelegate!T) { enum attrs = functionAttributes!T | FunctionAttribute.pure_; return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; } int i = 0; void foo() { ++i;// foo accesses mutable module-level data } void bar() pure { auto pureFoo = assumePure(); pureFoo();// <-- pure function is calling impure function } void main() { assert(i == 0); bar(); assert(i == 1);// mutation through a pure function } It also came up in other discussions (the keyword is `assumePure`), e.g. - http://forum.dlang.org/post/hpxxghbiomtitrmwe...@forum.dlang.org - http://forum.dlang.org/post/nfhqvffqtkfsxjewg...@forum.dlang.org
Re: purity question
On Sunday, May 28, 2017 18:46:22 Brad Roberts via Digitalmars-d-learn wrote: > Again, of course it's possible to do it wrong. Escape hatches are like > that. And of course things are being worked on and improved, I'm one of > the ones that's done a good bit of that at various points in time. I'm > really not seeking a lesson or lecture in why it's dangerous. Sorry, if I'm coming across like I'm lecturing you. I'm not trying to. I'm trying to be clear about the situation, and because this is on D.Learn, with something like this, I don't want to show how to get around the issue without clearly warning about the risks, because even if you know exactly what you're doing when casting to get around purity problems, many others reading this list will not. - Jonathan M Davis
Re: purity question
On 5/28/2017 6:46 PM, Brad Roberts via Digitalmars-d-learn wrote: Here's the bug that I'm digging into today, a clear example of an api that _should_ be pure, but based on the implementation is rather difficult for the compiler to infer. https://issues.dlang.org/show_bug.cgi?id=17442
Re: purity question
On 5/28/2017 6:36 PM, Jonathan M Davis via Digitalmars-d-learn wrote: On Sunday, May 28, 2017 17:53:25 Brad Roberts via Digitalmars-d-learn wrote: On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn wrote: On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn wrote: Is there a mechanism for declaring something pure when it's built from parts which individually aren't? string foo(string s) { // do something arbitrarily complex with s that doesn't touch globals or change global state except possibly state of the heap or gc return s; } you can cast Ok, so there essentially isn't. I'm well aware of the risks of lying to the compiler, but it's also not sufficiently smart to unravel complex code. Combined with there being interesting parts of the standard libraries that themselves aren't marked pure, there's a real need for escape hatches. Well, the big thing is that there are a number of functions in Phobos that need to have someone go over them and fix them so that they are inferred to be pure when they should be. Some work has been done in that area, and the situation is certainly better than it once was, but not enough work has been done to make it so that we can at all reasonably that everything in Phobos that should be pure is and what isn't shouldn't be. A simple example: anything that has a malloc/free pair. Yeah, if you do it right, you should be fine, but you have to do it right, and it's very easy to miss some detail that makes it wrong to insist to the compiler that what you're doing is pure. So, arguably, having anything more user-friendly than a cast is pretty dangerous. Having folks slap something like @assume_pure on things (if there were such an attribute) could be pretty risky. We already have enough trouble with @trusted on that front. Ultimately, we want to be in a position where needing to get around pure is very rare, and in most cases, I'd just tell folks to give up on pure on that piece of code rather than trying to hack it into working. - Jonathan M Davis Again, of course it's possible to do it wrong. Escape hatches are like that. And of course things are being worked on and improved, I'm one of the ones that's done a good bit of that at various points in time. I'm really not seeking a lesson or lecture in why it's dangerous. Here's the bug that I'm digging into today, a clear example of an api that _should_ be pure, but based on the implementation is rather difficult for the compiler to infer.
Re: purity question
On Sunday, May 28, 2017 18:39:02 Brad Roberts via Digitalmars-d-learn wrote: > On 5/28/2017 6:27 PM, Jonathan M Davis via Digitalmars-d-learn wrote: > > There was a whole discussion or 3 is PRs about making malloc pure, and > > IIRC, it was done and then decided that it wasn't safe to do some for > > one reason or another (IIRC, it had to do with what would happen when > > calls were elided, because the caller was strongly pure, but I'm not > > sure). So, I'd be _very_ careful about deciding that it was safe to > > call malloc in pure code. I expect that it's just fine in some > > contexts, but it's easy enough to screw up and mark something as pure > > when it really shouldn't be because of some detail you missed that you > > should be _really_ careful about decided to cast to pure. > > That's one reason I explicitly referenced malloc/free pairs. It's a lot > easier to be sure that those together aren't violating purity. Agreed. But it's the intricacies like that which make having a clean backdoor for pure a bit dangerous, much as it would be nice when you actually need to do it and know what you're doing well enough to get it right. - Jonathan M Davis
Re: purity question
On 5/28/2017 6:27 PM, Jonathan M Davis via Digitalmars-d-learn wrote: On Monday, May 29, 2017 01:01:46 Stefan Koch via Digitalmars-d-learn wrote: On Monday, 29 May 2017 at 00:53:25 UTC, Brad Roberts wrote: On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn wrote: On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn wrote: Is there a mechanism for declaring something pure when it's built from parts which individually aren't? string foo(string s) { // do something arbitrarily complex with s that doesn't touch globals or change global state except possibly state of the heap or gc return s; } you can cast Ok, so there essentially isn't. I'm well aware of the risks of lying to the compiler, but it's also not sufficiently smart to unravel complex code. Combined with there being interesting parts of the standard libraries that themselves aren't marked pure, there's a real need for escape hatches. A simple example: anything that has a malloc/free pair. There is void[] myPureMalloc(uint size) pure @trusted nothrow @nogc { alias pure_malloc_t = pure nothrow void* function(size_t size); return (cast(pure_malloc_t)malloc)(size)[0 .. size]; } There was a whole discussion or 3 is PRs about making malloc pure, and IIRC, it was done and then decided that it wasn't safe to do some for one reason or another (IIRC, it had to do with what would happen when calls were elided, because the caller was strongly pure, but I'm not sure). So, I'd be _very_ careful about deciding that it was safe to call malloc in pure code. I expect that it's just fine in some contexts, but it's easy enough to screw up and mark something as pure when it really shouldn't be because of some detail you missed that you should be _really_ careful about decided to cast to pure. - Jonathan M Davis That's one reason I explicitly referenced malloc/free pairs. It's a lot easier to be sure that those together aren't violating purity.
Re: purity question
On Sunday, May 28, 2017 17:53:25 Brad Roberts via Digitalmars-d-learn wrote: > On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn wrote: > > On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn wrote: > >> Is there a mechanism for declaring something pure when it's built from > >> parts which individually aren't? > >> > >> string foo(string s) > >> { > >> > >> // do something arbitrarily complex with s that doesn't touch > >> > >> globals or change global state except possibly state of the heap or gc > >> > >> return s; > >> > >> } > > > > you can cast > > Ok, so there essentially isn't. I'm well aware of the risks of lying to > the compiler, but it's also not sufficiently smart to unravel complex > code. Combined with there being interesting parts of the standard > libraries that themselves aren't marked pure, there's a real need for > escape hatches. Well, the big thing is that there are a number of functions in Phobos that need to have someone go over them and fix them so that they are inferred to be pure when they should be. Some work has been done in that area, and the situation is certainly better than it once was, but not enough work has been done to make it so that we can at all reasonably that everything in Phobos that should be pure is and what isn't shouldn't be. > A simple example: anything that has a malloc/free pair. Yeah, if you do it right, you should be fine, but you have to do it right, and it's very easy to miss some detail that makes it wrong to insist to the compiler that what you're doing is pure. So, arguably, having anything more user-friendly than a cast is pretty dangerous. Having folks slap something like @assume_pure on things (if there were such an attribute) could be pretty risky. We already have enough trouble with @trusted on that front. Ultimately, we want to be in a position where needing to get around pure is very rare, and in most cases, I'd just tell folks to give up on pure on that piece of code rather than trying to hack it into working. - Jonathan M Davis
Re: purity question
On Monday, May 29, 2017 01:01:46 Stefan Koch via Digitalmars-d-learn wrote: > On Monday, 29 May 2017 at 00:53:25 UTC, Brad Roberts wrote: > > On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn > > > > wrote: > >> On Sunday, May 28, 2017 16:49:16 Brad Roberts via > >> > >> Digitalmars-d-learn wrote: > >>> Is there a mechanism for declaring something pure when it's > >>> built from > >>> parts which individually aren't? > >>> > >>> string foo(string s) > >>> { > >>> > >>> // do something arbitrarily complex with s that doesn't > >>> > >>> touch > >>> globals or change global state except possibly state of the > >>> heap or gc > >>> > >>> return s; > >>> > >>> } > >> > >> you can cast > > > > Ok, so there essentially isn't. I'm well aware of the risks of > > lying to the compiler, but it's also not sufficiently smart to > > unravel complex code. Combined with there being interesting > > parts of the standard libraries that themselves aren't marked > > pure, there's a real need for escape hatches. A simple > > example: anything that has a malloc/free pair. > > There is > > void[] myPureMalloc(uint size) pure @trusted nothrow @nogc > { > alias pure_malloc_t = pure nothrow void* function(size_t size); > return (cast(pure_malloc_t)malloc)(size)[0 .. size]; > } There was a whole discussion or 3 is PRs about making malloc pure, and IIRC, it was done and then decided that it wasn't safe to do some for one reason or another (IIRC, it had to do with what would happen when calls were elided, because the caller was strongly pure, but I'm not sure). So, I'd be _very_ careful about deciding that it was safe to call malloc in pure code. I expect that it's just fine in some contexts, but it's easy enough to screw up and mark something as pure when it really shouldn't be because of some detail you missed that you should be _really_ careful about decided to cast to pure. - Jonathan M Davis
Re: purity question
On Monday, 29 May 2017 at 01:12:53 UTC, Era Scarecrow wrote: ... Hmm didn't notice the post had split. Otherwise i wouldn't have replied... That and thinking about the GC state (outside of allocating memory)...
Re: purity question
On Sunday, 28 May 2017 at 23:49:16 UTC, Brad Roberts wrote: // do something arbitrarily complex with s that doesn't touch globals or change global state except possibly state of the heap or gc Sounds like the basic definition of pure to me; At least in regards to D. Memory allocation which is a system call, doesn't actually break purity. Then again if you were worried about not using the gc, there's the newer nogc property. [quote] TDPL pg. 165: 5.11.1 Pure functions In D, a function is considered pure if returning a result is it's only effect and the result depends only on the function's arguments. [/quote]
Re: purity question
On Monday, 29 May 2017 at 01:01:46 UTC, Stefan Koch wrote: There is void[] myPureMalloc(uint size) pure @trusted nothrow @nogc { import core.stdc.stdlib : malloc; alias pure_malloc_t = @nogc pure nothrow void* function(size_t size); return (cast(pure_malloc_t))(size)[0 .. size]; } This is the fixed version.
Re: purity question
On 5/28/2017 6:01 PM, Stefan Koch via Digitalmars-d-learn wrote: On Monday, 29 May 2017 at 00:53:25 UTC, Brad Roberts wrote: On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn wrote: On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn wrote: Is there a mechanism for declaring something pure when it's built from parts which individually aren't? string foo(string s) { // do something arbitrarily complex with s that doesn't touch globals or change global state except possibly state of the heap or gc return s; } you can cast Ok, so there essentially isn't. I'm well aware of the risks of lying to the compiler, but it's also not sufficiently smart to unravel complex code. Combined with there being interesting parts of the standard libraries that themselves aren't marked pure, there's a real need for escape hatches. A simple example: anything that has a malloc/free pair. There is void[] myPureMalloc(uint size) pure @trusted nothrow @nogc { alias pure_malloc_t = pure nothrow void* function(size_t size); return (cast(pure_malloc_t)malloc)(size)[0 .. size]; } That's still a cast. It's a nice way to isolate the cast, but it's clearly still there. And as I said, that's just one simple example.
Re: purity question
On Monday, 29 May 2017 at 00:53:25 UTC, Brad Roberts wrote: On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn wrote: On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn wrote: Is there a mechanism for declaring something pure when it's built from parts which individually aren't? string foo(string s) { // do something arbitrarily complex with s that doesn't touch globals or change global state except possibly state of the heap or gc return s; } you can cast Ok, so there essentially isn't. I'm well aware of the risks of lying to the compiler, but it's also not sufficiently smart to unravel complex code. Combined with there being interesting parts of the standard libraries that themselves aren't marked pure, there's a real need for escape hatches. A simple example: anything that has a malloc/free pair. There is void[] myPureMalloc(uint size) pure @trusted nothrow @nogc { alias pure_malloc_t = pure nothrow void* function(size_t size); return (cast(pure_malloc_t)malloc)(size)[0 .. size]; }
Re: purity question
On 5/28/2017 5:34 PM, Jonathan M Davis via Digitalmars-d-learn wrote: On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn wrote: Is there a mechanism for declaring something pure when it's built from parts which individually aren't? string foo(string s) { // do something arbitrarily complex with s that doesn't touch globals or change global state except possibly state of the heap or gc return s; } you can cast Ok, so there essentially isn't. I'm well aware of the risks of lying to the compiler, but it's also not sufficiently smart to unravel complex code. Combined with there being interesting parts of the standard libraries that themselves aren't marked pure, there's a real need for escape hatches. A simple example: anything that has a malloc/free pair.
Re: purity question
On Sunday, May 28, 2017 16:49:16 Brad Roberts via Digitalmars-d-learn wrote: > Is there a mechanism for declaring something pure when it's built from > parts which individually aren't? > > string foo(string s) > { > // do something arbitrarily complex with s that doesn't touch > globals or change global state except possibly state of the heap or gc > return s; > } Either everything the function is doing is pure, or it can't be pure without cheating, and cheating is usually a bad idea. There is no @trusted equivalent for pure. Now, if you're absolutely sure that the function doesn't do anything that's actually going to run afoul of pure and what the compiler will assume about pure functions, but it's using stuff that wasn't properly marked as pure, or does something that can't be marked as pure for some reason but in this context would actually be fine, you can cast. For instance, std.datetime's LocalTime is a singleton which gets the timezone conversion from the system. Its operations can't be pure, but it can be constructed as pure, and because it's a singleton, it will return the same instance every time. Originally, a static constructor was used to initialize the singleton (which therefore worked great with pure), but because of circular dependency pain with static constructors, we had to get rid of the static constructors in std.datetime. The solution was to move the initialization into another function which was not pure, and then when we call it from theh singleton function, we cast it to pure: static immutable(LocalTime) opCall() @trusted pure nothrow { alias FuncType = @safe pure nothrow immutable(LocalTime) function(); return (cast(FuncType))(); } static immutable(LocalTime) singleton() @trusted { import core.stdc.time : tzset; import std.concurrency : initOnce; static instance = new immutable(LocalTime)(); static shared bool guard; initOnce!guard({tzset(); return true;}()); return instance; } Now, in the vast majority of cases, I would strongly advise against cheating in this manner, because you need to be absolutely sure that you're not actually violating pure (particularly what the compiler will assume about pure) by what you're doing, and usually what it comes down to is that something that your function is calling needs to be fixed so that it can be inferred as pure, or it simply needs to be marked as pure. Casting is a horrible hack that should only be used as a last resort and only when you're 100% sure that it's fine. However, you _can_ use casting as a backdoor if you really have to. - Jonathan M Davis