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

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu Apr 18 14:52:53 UTC 2019


On Thursday, April 18, 2019 4:05:32 AM MDT Eugene Wissner via Digitalmars-d 
wrote:
> 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.

pure means that the function does not access mutable, global state except
via the function's arguments. That's it. nd the compiler _is_ able to do
stuff with that and does so right now. It will elide function calls (though
under such restricted circumstances that it's not really worth it), but the
bigger gains come from how it helps the type system. For instance,

Foo* foo(int i) pure { return new Foo(i); }

immutable f = foo();

compiles just fine, because the compiler is able to determine that the
return value of foo is unique and that therefore the cast is safe. That
allows for far more complicated functions which can be used to create
immutable objects without requiring any explicit casting. pure has been
improved in a number of small ways like that over time as people have
figured out how to make the compiler better leverage the guarantees that
pure provides.

If what you're looking for is to be able to elide function calls all over
the place, then I doubt that you'll ever get that - if nothing else, because
that would require more code flow analysis than Walter is typically willing
to allow. Something like func(42) * func(42) will result in a call being
elided if func is pure, but even splitting it up onto two lines kills that.
e.g.

auto f = func(42);
f = func(42) * f;

won't elide anything and likely never will. As such, the gains come from
pure tend to be very localized, and slapping it on everything is going to
tend to be of minimal benefit. It does ensure that such code doesn't access
non-immutable globals if they're not passed to it, which might be nice to
know, but how useful it is is debatable, and it makes it a royal pain if you
need that code to access globals later for caching or I/O or whatever.

> 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.

Except that most programs aren't written in a way that it makes any sense
for the majority of their code to be marked as pure. I/O alone tends to kill
that.

> 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.

I don't get this. pure is fairly well understood. The guarantees that it
provides are well understood. The only thing that's really changed over time
(other than the introduction of "weak" purity, which is what made it so that
pure is really just @noglobal rather than having anything to do with
functional purity) is the set of things that the compiler is able to do
based on the fact that a function is marked with pure. That list keeps
getting longer (if slowly). It started with function call elision (based on
"strong" purity, which is what pure had been originally) and has grown over
time. As far as writing code goes, the hard part is when you try to write
code that the compiler doesn't think is pure, but you're trying to write it
in a way that it maintains the guarantees that the compiler makes with pure
and bases its assumptions off of. _That_ can be hard to do, because it means
fully understanding what the compiler will do based on pure, and that list
grows over time. So, it's almost never something that really makes sense.

But if you're just using pure without trying to do any casts, just letting
the compiler determine what's pure and isn't (either by using templates or
by having it yell at you when you mark something that's pure that isn't),
it's very straightforward. It works exactly as intended, and plenty of
people know how it's supposed to work. The only real problem with it is its
name, because newcomers think that it's all about functional purity, which
it really isn't. It's just providing a set of guarantees about a function
that the compiler can build on, and one of the things that it can do is
figure out that a function is functionally pure and then do stuff like elide
calls - but it can do more than that (like implicitly casting to immutable
under some circumstances), and that other stuff that it can do doesn't
necessarily have much to do with functional purity, just the knowledge that
the only arguments to the function are the ones passed to it.

- Jonathan M Davis





More information about the Digitalmars-d mailing list