`@safe` by default. What about `@pure` and `immutable` by default?

Eugene Wissner belka at caraus.de
Thu Apr 18 10:05:32 UTC 2019


On Thursday, 18 April 2019 at 08:18:05 UTC, Jonathan M Davis 
wrote:
> On Wednesday, April 17, 2019 1:25:24 AM MDT Eugene Wissner via 
> Digitalmars-d wrote:
>> On Tuesday, 16 April 2019 at 21:33:54 UTC, Jonathan M Davis 
>> wrote:
>> > pure can be nice when it works, but really basic things like 
>> > I/O don't work if pure is used. You can get around that for 
>> > debugging with debug blocks, but if you have a bunch of 
>> > code, and it turns out that you need to do something in it 
>> > that isn't pure, you'd be screwed unless you go and mark a 
>> > ton of code with impure (or whatever the opposite of pure 
>> > would be). It's not like you can just opt-out in the middle 
>> > like you can with @safe by using @trusted to use @system 
>> > code.
>>
>> I/O doesn't work in pure code, because I/O isn't pure. And if 
>> you want an impure statement in pure code, you can just cast 
>> purity away same as pureMalloc does it.
>
> Of course, I/O isn't pure, and casting with pure is almost 
> always the wrong thing to do. It's like const. If you cast it 
> away and mutate the value, then the const is a lie, and the 
> assumptions that the compiler makes are wrong, which could 
> result in wrong code. In the case of pure and I/O, casting 
> could easily mean that I/O isn't done which the code expects to 
> be done, because the compiler decided that a function didn't 
> need to be called multiple times, because it was pure, and the 
> result would be the same. Similarly, if the compiler is lied to 
> about pure, that can really screw with immutable, because the 
> compiler is able to make assumptions based on the fact that the 
> code is pure and determine that some data has to be unique, 
> because it could not have possibly be passed into the function 
> and thus had to have been allocated within the function.
>
> pureMalloc is one case where it's arguably okay to cast with 
> regards to pure - but there's been a lot of discussion about 
> that, because it's incredibly easy to screw up the compiler 
> guarantees in the process. It works with the GC, because of the 
> extra control that the compiler has and the fact that 
> programmer isn't the one that has to deal with freeing the 
> memory.
>
> In general, if code is casting with regards to pure, it's 
> almost certainly wrong. Exceptions to that exist, but they're 
> extremely rare, and they must be done _very_ carefully to avoid 
> running afoul of compiler guarantees.
>
>> pure doesn't make any sense if it isn't default. Plenty of 
>> people for a valid reason don't care about the attributes. As 
>> soon as you have dependencies, you can't mark your own code as 
>> pure because you use some dependencies that may be 100% pure 
>> but aren't annotated as such. It  is possible to write 
>> pure-annotated code only if you have not-invented-here-syndrom 
>> like me and have no dependencies.
>>
>> Pure should either be default or be completely removed, it is 
>> absolutely useless as it is today.
>
> If pure were the default, then you would have to turn it off on 
> main on pretty much every program ever written. That should 
> tell you something. The only programs which could avoid having 
> main be pure would be those whose only input is the arguments 
> to main and whose only output was the return value from main or 
> a thrown exception that escapes main. And that's _very_ few 
> programs.
>
> For code to work as pure, it needs to be written with that in 
> mind and then cannot add stuff like I/O or caching later 
> (caching could be added in _some_ cases when the cache is 
> within a variable, but certainly, something like Phobos' 
> memoize wouldn't work). To try and force a program in general 
> to be pure is to be playing the same insane game that languages 
> like Haskell play. Sure, D's pure isn't quite the same thing 
> (it really should be @noglobal at this point, not pure), but 
> the effect at the call site is the same, and many of the same 
> restrictions within a function still exist even if they're more 
> relaxed.
>
> I'll grant you that it can be annoying to use pure when a 
> library you depend on doesn't use it properly, but by forcing 
> it everywhere, the net result will be that code all over the 
> place will have to be marked with impure (or @global) in order 
> to work properly. And it's likely to be very common that you'd 
> then have to go through large portions of code and add @global 
> all over the place in order to add a piece of functionality 
> that you need that relates to I/O or caching or some other use 
> case where you need to interact with mutable data that isn't 
> passed in as an argument.
>
> pure works well when it is in smaller sections of code which 
> were specifically written to be pure, IMHO, it's a disaster if 
> you try to mark your entire program that way. It's just too 
> easy to run into situations where you can't have a piece of 
> code be pure. It's a problem similar to requiring const or 
> immutable everywhere, only instead of restricting itself to 
> specific pieces of data, it invades the entire call stack. It 
> does work in some programs that are specifically written that 
> way, but for a lot of programs it won't - especially programs 
> that are not written in a functional manner. Trying to force an 
> attribute like const, immutable, or pure as the default is 
> effectively trying to force D code to be written in a 
> functional manner instead of treating it as fully 
> multiparadigm, and it's forcing a paradigm that is very much 
> not in line with the languages that D grew from or with how D's 
> standard libraries and common idioms currently work. D code is 
> typically more functional in nature than other languages that 
> stem from C/C++, but it's still very much an imperative, 
> systems-level language.
>
> - Jonathan M Davis

As it is now, pure is not like immutable or const, it is like 
@safe, it is just an annotation that tells, whether the code does 
something you don't expect. Nobody knows if pure will mean one 
day more or something different, probably not, because even 
official libraries break purity.

I always have to annotate the main function as impure. So what? 
It is still better to annotate a few functions as impure than the 
most code as pure.

Casting away purity was meant by me only for debugging and 
similar things, not as permanent solution. Even Haskell provides 
perfomUnsafeIO for that. And if a pure function becomes one day 
impure, the code needs refactoring anyway. But I see, there is no 
way for someone who doesn't care about purity to disable it. 
Maybe compiler should provide some switch that disables purity, 
nogc and so forth for the code and all its dependencies.

I actually agree with you that D is an imperative programming 
language and as I said above I think pure brings more harm than 
good and I could live without it at all. It is just there, it 
doesn't work as is and of course nobody knows, how it is supposed 
to work.


More information about the Digitalmars-d mailing list