<p>By options of purity, I specifically am talking about global properties across the entire language such as all variables being immutable, all functions being pure, <a href="https://mlochbaum.github.io/BQN/index.html" rel="nofollow noreferrer">BQN</a>'s having all primitives be pure and terminate, or to some extent <a href="https://www.aplwiki.com/" rel="nofollow noreferrer">APL</a>'s &quot;everything is an array&quot;.</p>
<p>What these properties do is effectively limit the programmer/implementer, and I was wondering if this hurts or helps, and in what ways.
I personally prefer functional style, with pure functions and immutable data, but it is limiting and most popular mainstream languages seem to allow as much as possible such as Javascript, PHP, and Python.</p>

<language-design>
Options for purity, and advantages/disadvantages of each option?
CC BY-SA 4.0
2023-06-04T21:09:40.003Z
Other relevant question: [What are the advantages and disadvantages of immutable languages?](https://languagedesign.stackexchange.com/q/444/89)
kaya3
<p>Another option, of course, is to allow controlled impurity, such as <code>unsafePerformIO</code> in Haskell.</p>
<p>Here's the approach we took in <a href="https://mercurylang.org/" rel="noreferrer">Mercury</a>.</p>
<p>Mercury is a logic language, like Prolog, but with strong types, strong modes, and strong determinism. When it came to implementing a foreign function interface, we decided that <a href="https://www.mercurylang.org/information/doc-latest/mercury_ref/Impurity.html" rel="noreferrer">this should have strong compile-time guarantees</a>, too.</p>
<p>Mercury defines three levels of purity:</p>
<ul>
<li>A procedure is <code>pure</code> when the set of solutions (i.e. its &quot;outputs&quot;) depends only on the input arguments. Note that this doesn't mean that it can't do I/O! Mercury has linear modes which allow a pure way to perform I/O, similar to linear types in Clean or monads in Haskell, and strong determinism ensures that you can't backtrack over I/O.</li>
<li>A procedure is <code>semipure</code> if it is pure, but its semantics may be affected by impure predicates.</li>
<li>A procedure is <code>impure</code> if it may perform I/O or modify hidden state without declaring it in its interface.</li>
</ul>
<p>You can think of <code>semipure</code> as meaning that it can &quot;read&quot; hidden state, but not &quot;write&quot; it.</p>
<p>No procedure, even an impure one, may change the declarative semantics of pure code. That is, it must honour its type/mode/determinism declaration.</p>
<p>Every place where an impure or semipure procedure is called must be annotated:</p>
<pre><code>impure_predicate(X,Y,Z) :-
    this_is_a_pure_call(X,Y),
    impure this_is_not(Y,Z),
    % etc etc
</code></pre>
<p>You can also make a promise to the compiler that this impure code is actually used in a pure way:</p>
<pre><code>pure_predicate(X,Y,Z) :-
    this_is_a_pure_call(X,Y),
    % The inside of this goal is impure, but the outside is pure.
    promise_pure (
        impure this_is_not(Y,Z)
    ),
    % etc etc
</code></pre>
<p>You can also promise purity at the procedure level via a pragma.</p>
<p>Promises of course can't be checked, but as Henry Baker rightly said, if you lie to your compiler, it will get its revenge.</p>

CC BY-SA 4.0
2023-05-16T23:34:39.980Z
2023-05-16T23:34:39.980Z
Pseudonym
CC BY-SA 4.0
2023-05-17T00:35:02.220Z
I love this. Makes it obvious to a reader where code is pure and where it isn't. I have had issues with python when modifying code and assuming functions are pure, and then get greeted with awful errors. Thanks for the answer :)
Brian BED
# Options for purity, and advantages/disadvantages of each option?
Posted by: username_0 on 2023-05-16
By options of purity, I specifically am talking about global properties across the entire language such as all variables being immutable, all functions being pure, [BQN](https://mlochbaum.github.io/BQN/index.html)'s having all primitives be pure and terminate, or to some extent [APL](https://www.aplwiki.com/)'s "everything is an array".

What these properties do is effectively limit the programmer/implementer, and I was wondering if this hurts or helps, and in what ways.
I personally prefer functional style, with pure functions and immutable data, but it is limiting and most popular mainstream languages seem to allow as much as possible such as Javascript, PHP, and Python.
**Tags:** language-design
> **Comments:**
> **username_1**: Other relevant question: [What are the advantages and disadvantages of immutable languages?](https://languagedesign.stackexchange.com/q/444/89)  
---
## 1 answers
### Answer by username_2 on 2023-05-16. Score: 6 | Accepted answer
Another option, of course, is to allow controlled impurity, such as unsafePerformIO in Haskell.
Here's the approach we took in Mercury.
Mercury is a logic language, like Prolog, but with strong types, strong modes, and strong determinism. When it came to implementing a foreign function interface, we decided that this should have strong compile-time guarantees, too.
Mercury defines three levels of purity:

A procedure is pure when the set of solutions (i.e. its "outputs") depends only on the input arguments. Note that this doesn't mean that it can't do I/O! Mercury has linear modes which allow a pure way to perform I/O, similar to linear types in Clean or monads in Haskell, and strong determinism ensures that you can't backtrack over I/O.
A procedure is semipure if it is pure, but its semantics may be affected by impure predicates.
A procedure is impure if it may perform I/O or modify hidden state without declaring it in its interface.

You can think of semipure as meaning that it can "read" hidden state, but not "write" it.
No procedure, even an impure one, may change the declarative semantics of pure code. That is, it must honour its type/mode/determinism declaration.
Every place where an impure or semipure procedure is called must be annotated:
impure_predicate(X,Y,Z) :-
    this_is_a_pure_call(X,Y),
    impure this_is_not(Y,Z),
    % etc etc

You can also make a promise to the compiler that this impure code is actually used in a pure way:
pure_predicate(X,Y,Z) :-
    this_is_a_pure_call(X,Y),
    % The inside of this goal is impure, but the outside is pure.
    promise_pure (
        impure this_is_not(Y,Z)
    ),
    % etc etc

You can also promise purity at the procedure level via a pragma.
Promises of course can't be checked, but as Henry Baker rightly said, if you lie to your compiler, it will get its revenge.
> **Comments:**
> **username_0** (2023-05-17): I love this. Makes it obvious to a reader where code is pure and where it isn't. I have had issues with python when modifying code and assuming functions are pure, and then get greeted with awful errors. Thanks for the answer :)  
---
