Typescript: add a modifier for pure functions

Created on 1 Apr 2016  Â·  40Comments  Â·  Source: microsoft/TypeScript

This is what pure means:

  1. no destructive operations on parameters
  2. all parameters must be guaranteed from being changed from the outside (immutable?)
  3. no calls to any other callback/function/method/constructor that doesn't have the pure modifier
  4. no reads from mutable values from a scope the pure function is closed over
  5. no writes to values in the closed scope
Needs Proposal Suggestion

Most helpful comment

Proposal draft : the pure modifier

I will suggest using the name pure instead of using reader to have more clarification.
For example :

var mutableVar = 1;
const obj = { a: "hi" };
let func = () => { mutableVar = 3 };

pure function doSomething() {
    mutableVar = 2; // Error
    obj.a = "bye"; // Error
    func(); // Error
}

doSomething();

So the metaphor goes like this :

  • By default, any function in TypeScript maybe dirty (means they may have side effects)
  • But, if a function is marked with pure, then it shall be pure (no side effects) or else the compiler will throw error

Regarding function call

  • To make it easy, all pure function can only call pure function.
  • Calling a function that is not marked pure should be an error even though the function does not bring any side effects.
  • For example:
function add(x: int, y: int){
    return x + y;
}

pure function getMyName(){
    return "Spongebob";
}

pure function doSomething() {
    var x = add(1, 2); // Error
    var s = getMyName(); // No error
}

Regarding I/O operations

  • Since pure function should have no side effects, it is also reasonable that a pure function should not call any I/O functions.
  • Actually, it is also due to the reason that I/O functions are not possibly marked as pure
  • For example :
pure function sayCheese(){
    console.log("Cheese!") //Error
}

Regarding pure classes

* Classes cannot be marked as pure as this violates the whole purpose of object-oriented paradigm, as classes are meant to store state.

  • Classes can be mark as pure (as suggested by @Dessix and @LewisAndrewCampbell), however it must satisfied all of the following characteristics:

    • Every field in the class is readonly

    • Every method is pure

For example, the following is a valid pure class:

pure class Color {
  public readonly red  : number;
  public readonly green: number;
  public readonly blue : number;

  public constructor(red:number, green:number, blue:number) {
    this.red   = red;
    this.green = green;
    this.blue  = blue;
  }

  public pure darker(factor = 0.25): Color {
    return new Color(
      this.red   * factor, 
      this.green * factor,
      this.blue  * factor
    );
  }

}

Regarding pure methods

  • Methods within a class can be marked as pure :
class Example {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    pure sayHello() {        
        this.greeting += "!!!!"; // Error as pure method cannot modify variable outside of own scope
        return this.greeting; // No error
    }
}
  • Constructor cannot be mark as pure.
  • pure methods behave just like pure function, they can only call other function or method that is mark pure and vice versa.
  • For example :
class Starfish {
    name: string;
    constructor(name: string) {
        this.name = name;
    }

    shout() {
        return "Hey I'm Patrick";
    }

    pure jump() {
        return "How can I jump?";
    }      
}

class Sponge {
    friend: StarFish;
    constructor(friend: Patrick){
        this.friend = friend;
    }

    pure play(){
        friend.shout(); // Error because shout() is not pure
        friend.jump(); // No error as jump() is pure
    }
}   
 ```
 * Based on the rule above, a function is also allowed to call a pure method of  a class. 

## Final words
If you have a C++ experience, you'll notice that `pure` is actually the [`const` modifier](http://www.geeksforgeeks.org/const-member-functions-c/). For example, a pure function/method in C++ would be declare as such: 
```cpp
class Example {
    private:
        string name;
    public:
        string GetName() const {
            return name;
        }
}

As a good practice, methods/function should be marked as pure whenever possible so that the process of debugging will be as smooth as jelly.

Suggestion

If this pure modifier is implemented, the TypeScript linter TSLint should suggest coder to mark any function that does not have side effect as pure.

Changelog

  • Added description for pure class. (2018-05-15)

All 40 comments

We need more information about what you would expect this to do

A pure function does still not guarantee that a callback is invoked immediately:

function wrap(f: () => void) {
    return { f };
}
let x = Math.random() > 0.5 ? 'hey' : 1;
let obj: { f: () => void };
if (typeof x === 'number') {
    obj = wrap(() => x + 2);
}
x = '';
obj.f();

In my opinion, a better idea would be to narrow constant variables only in callbacks, as that would be sound.

I'd then say that only the callback has to be pure. In your example, narrowing could even happen when map is not pure, as it cannot modify the value of x (and if it would, the callback wouldn't be pure at all).

This would be boon to productivity, and would afford developers a practical tool to isolate side effects

const declaration + readonly modifier give us this:

no variables defined in the outside scope that are proven to possibly mutate can be used in a pure function

@edevine, i would say it means

"const declaration + readonly modifier" for all sub-objects all the way down to the last primitives

For context we have discussed similar proposals as part of the readonly modifier support. see https://github.com/Microsoft/TypeScript/issues/6614 for more information.

related #8381

A pure function does still not guarantee that a callback is invoked immediately:

Little bit OT, but I would like if TS can add an immediate modifier also to resolve the above problem:

declare interface Array<a> {
   map<b>(map: immediate (value: a) => b): b[]: 
}
let x = Math.random() > 0.5 ? 'hey' : 1;
if (typeof x === 'number') {
    [].map(() => x + 2); // no error callback is called immediately
}

function f(f: immediate () => void) {
    return { f }; // error callback must be called.
}

function f(f: immediate () => void) {
    return fs.readFileAsync(f); // error cannot call immediate callback async.
}

@aleksey-bykov

'Pure' function means (wikipedia):

  1. The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change while program execution proceeds or between different executions of the program, nor can it depend on any external input from I/O devices (usually—see below).
  2. Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices (usually—see below).

In order to satisfy (1.) this would mean it would also exclude read operations to _any_ captured entity and global scope entity (window, document, etc.). The only remotely possible exceptions here are captured constants with a primitive type like number, string, boolean etc., however even they can technically have properties, so even if property access is prevented on them, they could still be returned from the function, and potentially the caller would receive a different return value between calls. This means the function couldn't basically read or write from/to anything outside of it, including captured variables (or possibly constants) from _another pure function_, as those are not guaranteed to have the same value at each execution.

It may be that the analysis is actually _easier_ than the one that's needed for #8353, but I'm not sure.. maybe it's safe to simply say it's 'different'.

Maybe the intention here wasn't really for 'pure' functions in the conventional sense, but a form of a non-side-effecting function, that could still return different values at each execution but is 'guaranteed' not to silently influence the state (including, say things like I/O). That would be closer to the analysis needed for #8353, but would include more components like modification of properties (which isn't really included there, it is only about reassignments), having an understanding of I/O related operations etc.

I can see possible side effects from getters being a problem, so this would mean that there should be some way to detect regular interface properties (not methods) that could still have side effects, so using readonly wouldn't be sufficient here:

interface MyInterface {
    readonly prop: number;    
}

class MyClass implements MyInterface {
    get prop(): number {
        mutateGlobalState();
        return 1;
    }
}

nonmutating function imSupposedToHaveNoSideEffects(arg: MyInterface) {
    let num = arg.prop;
}

imSupposedToHaveNoSideEffects(new MyClass())

It needs to be something like:

interface MyInterface {
    nonmutating readonly prop: number;    
}

(I'm using the nonmutating modifier here temporarily, just for illustration, perhaps there's a better one for this)

[_Edit: Modified the code example to make it a bit clearer_]
[_Edit: Or maybe the whole interface or class should be tagged as nonmutating?_]

this is what 3 is about: anything closed over by a pure function has to be
immutable, or else being pure should not typecheck
On May 1, 2016 5:14 AM, "malibuzios" [email protected] wrote:

I can see possible side effects from getters being a problem, so this
would mean that there should be some way to detect readonly interface
properties that could still have side effects, so using readonly wouldn't
be sufficient here:

interface MyInterface {
readonly prop: number;
}
class MyClass implements MyInterface{
get prop(): number {
mutateGlobalState();
return 1;
}
}

nonmutating function imSupposedToHaveNoSideEffects() {
let x = new MyClass();
let num = x.prop;
}

It needs to be something like:

interface MyInterface {
nonmutating readonly prop: number;
}

(I'm using the nonmutating modifier here temporarily, just for
illustration, perhaps there's a better one for this)

—
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
https://github.com/Microsoft/TypeScript/issues/7770#issuecomment-216029209

@aleksey-bykov

I'm sorry, I might have misunderstood, the intention wasn't very clear from the way it was described. You are right that other 'pure' functions can be called from 'pure' functions (I didn't mention function calls). But variables, properties or even whole classes and interfaces would need to somehow be 'deeply' immutable as well, otherwise it wouldn't really work. const and readonly don't guarantee immutability of properties.

const globalVar = { prop: 1 };

pure function func(): { prop: number } {
    return globalVar;
}

func(); // result is { prop: 1 }
globalVar.prop = 2;
func(); // result is { prop: 2 }

So what you mean is that there should be a 'deeper' form of immutablity, which also includes properties. This would either require something like a keyword or an immutable type trait for variables and members. I still don't have a great idea on how to model this, especially with anonymous object literals, maybe:

immutable globalVar = { prop: 1 };
globalVar.prop = 2; // error

Or a type trait:

const globalVar = <immutable> { prop: 1 }; // type of globalVar is 'immutable { prop: number }'
globalVar.prop = 2; // error

I also considered the fact that although strange, in Javascript even primitives may have properties, and const doesn't prevent these to be modified:

const x: number = 1;
x["secret"] = 123; // no error

_[I've tested this in both Firefox and Chrome and it doesn't error, however the resulting property value is undefined, same for let, so I might open an issue for this. I still need to check if this happens in all cases and what the standard says about this, though, both in strict and non-strict mode]_


Anyway, both pure (in the conventional sense) and nonmutating are useful ways to model different kinds of scenarios, so I'm also exploring the 'weaker' nonmutating variation (that although less 'safe' is more useful in practice) as well in the context of a (currently 'hypothetical') programming language I'm thinking about, but it may be interesting to share here, so bear with me:

A further question that needs to be considered is whether a non-mutating function should still be allowed to mutate external state through one of its arguments:

nonmutating function func(obj: { prop: number }) {
    obj.prop = 2;
}

var globalVar = { prop: 1 };

function example() {
    func(globalVar);
}

I believe this is may be unavoidable, so the answer would have to be 'yes' (this also means that nonmutating wouldn't be an ideally precise name for the modifier, since it can still mutate through arguments).

Trying to detect the passing of captured entities wouldn't really help here:

var globalVar = { prop: 1 };

function example() {
    let x = { a: globalVar };
    func(x.a); // This would be very difficult to reliably detect..
}

I was thinking of this in the context of ideas for a hypothetical programming language, that is still imperative and 'impure' but has strong safeguards when it comes to side-effects. Having this 'middle ground', where functions cannot have 'silent' side-effects but could still modify global state through arguments seemed like an interesting compromise.

However, this may become uncomfortable to the programmer, say, to have to pass the print function for any function that may print something to the display:

enclosed function iPrintStuff(printFunction: (message: string) => void, text: string) {
    printFunction(text);
}

iPrintStuff(console.log, "Hi");

One way of mitigating this (mostly for this 'hypothetical' language, but perhaps also relevant here) would be using a rather different way to organize how the program interacts with state. Perhaps the general pattern would be to use what I call 'enclosed' classes instead of functions, where the class would receive all the external entities that it would need for its internal operations during construction, but otherwise cannot silently influence external state.

enclosed class PrintHelper {
    constructor(private printFunction: (message: string) => void) {
    }

    print(message: string) {
        this.printFunction(message);
    }
}

let printHelper = new PrintHelper(console.log)

printHelper.print("hi");

This may seem somewhat strange or unneccsary, but it does provide a 'controlled' way to guarantee several properties that may be important from a design perspective, although there are probably more 'elegant' ways to model this, but would require some further syntax. One that I can think of is having a special rule that static constructors can reference captured variables as well as mutating functions like print:

declare var globalVar;

enclosed class PrintHelper {
    static private prop; 
    static private printFunction: (message: string) => void

    static constructor() {
        // These assignments are only allowed in the constructor:

        this.prop = globalVar;
        this.printFunction = console.log;
    }

    static print(message: string) {
        this.printFunction(message);
    }
}

PrintHelper.print("hi");

(I'm still in an early state of developing this..)

I think this may be better syntax. Here, the compiler can easily become aware and analyze what external state the class can possibly 'touch' (the foreign deceleration would be required in order to reference an external entity in the body of the class):

var globalVar = 1;

enclosed class PrintHelper {
    foreign globalVar; // This just captures 'globalVar' above, 
    foreign log = console.log; // This provides a local alias to 'console.log', 

    static updateGlobalVar() {
        globalVar = 2; // foreign references don't require 'this'
    }

    static print(message: string) {
        log(message);
    }
}

PrintHelper.print("hi");

_My intention is that foreigns can also possibly be set on construction, but I'm still working on how to do it elegantly._

A stronger version of this could also disallow all members of silently reading non-immutable external entities, so they would be closer to real 'pure' functions: Having no foreign members would essentially mean all of its methods are _almost_ pure, as they cannot have any side effects outside the boundaries of the class. Having no properties - only methods - would mean they are 'truly' pure, in the sense of providing referential transparency.

(By saying this I do assume here that these methods will only accept immutable arguments, however that restriction can also be weakened, so there's a range of possibilities here).

_Edit: Conceptually this seems somewhat like a hybrid between an isolated 'module' and a class. Sort of like an 'instantiable module' - when the class is not completely static, I mean._

_Edit: Reworked the example a bit for a more compact syntax._

The more I look at it, I start to feel that what I'm really 'looking' for here may be better expressed (at least in TypeScript) as an 'enclosed namespace', rather than a class, which would have _no_ access to non-immutable outside state (including the standard library and DOM) unless an explicit foreign declaration is used:

var globalVar = 1;

enclosed namespace Example {
    export function updateGlobalVar(value: number) {
        foreign globalVar; // This just captures 'globalVar' above 

        globalVar = value;
    }

    export function print(message: string) {
        foreign log = console.log; // This provides a local alias to 'console.log'

        log(message);
    }

    export function pureFunction(x: number) {
        return x + 1;
    }
}

The main problem here is convenience: how to avoid requiring the programmer to write many foreign declarations for each little thing they need from the outside context..

_Edit: I've decided to experiment with having the foreign declarations scoped just like type declarations and move them as close as possible to the position they are actually used (they essentially only serve to tell the compiler to 'import' something from outside of the 'fenced' namespace and are erased in the resulting compiled code)._

_Update: I've completely 'rebooted' the whole thing and started from scratch using a different approach, which is conceptually closer to what was originally proposed but concentrates on the weaker, but more useful 'spectator-only' mode rather than trying to achieve referential transparency. However, it will take some time to work out all the details, perhaps up several weeks to get this to something approaching a real 'full' proposal._

Proposal draft: the 'reader' modifier

Summary

The 'reader' modifier is a way to tell the compiler that a particular function, method, class or interface member does not induce any side-effects outside of its own internal scope. This is not, however, a sufficient condition to characterize the affected entity as 'pure' in the same sense that a 'pure' function would be, as it does not guarantee _referential transparency_, that is, the property that for a given input, the same output would be returned at all subsequent calls. It can be seen as a 'middle-ground' between the 'extreme' mutability of imperative languages and the extreme 'purity', or, non-mutability trait in some functional languages.

The purpose and usefulness of having this is both for programmers, to be able to create better and safer contracts, for themselves and for others, and compilers, to better reason about the code and become more closely aware of the intention of the programmer.

'Reader' functions

A reader function:

  1. Can 'spectate' its outside scope, this includes both constants and variables, which may either be primitives or objects. These are not required to be immutable or have any special declared modifier like readonly or const.
  2. Cannot mutate any variable or object property outside of itself.
  3. Can only call other reader functions or methods.
  4. Can only instantiate reader classes.
  5. Can only call the reader members of an external class instance or interface (this may be implicitly detected, see the 'open questions' section below).

A reader function or class constructor arguments may only include:

  1. Immutable values or objects.
  2. reader functions.
  3. Any class instance or interface, however it will be implicitly reduced only to its reader members.

Examples:

This wouldn't work (it guarantees 'no side effects')

var mutableVar = 1;
const obj = { a: "hi" };
let func = () => { mutableVar = 3 };

reader function doSomething() {
    mutableVar = 2; // Error
    obj.a = "bye"; // Error
    func(); // Error
}

doSomething();

But this would (no special guarantee for referential transparency):

var mutableVar = 1;

reader function doSomething(): number {
    let x = mutableVar; // this assignment is by value, so this would work
    x += 1; 

    return x;
}

doSomething(); // returns 2;

mutableVar++;

doSomething(); // returns 3;

'Reader' classes, class methods and interface members

A class annotated as a reader:

  1. Can 'spectate' the outside scope, similarly to a reader function.
  2. Cannot mutate any variable or property outside of itself.
  3. Similarly to a reader function, it can internally call reader functions, instantiate reader classes, and use reader members of an interface.

A class method annotated as a reader (which may be enclosed both in a reader or non-reader class), is very similar to a reader function.

  1. Can 'spectate' the outside scope.
  2. Cannot mutate any variable or property outside of itself, including its enclosing class' instance properties.
  3. Has all the other traits of reader functions.

A non-reader method in a reader class:

  1. Can 'spectate' the outside scope.
  2. Can mutate instance members.
  3. Cannot mutate anything outside of its class.

A reader interface member:

  1. Guarantees the same behavior as would be expected from reader function or class method.

Examples:

interface DOM {
    reader getNode(path: string): DOMNode
    setNode(path: string, value: DOMNode)

    // ...
}

declare var dom: DOM;

declare reader class Dictionary<T> {
    reader lookup(key: string): T;
    reader clone(): Dictionary<T>;
    add(key: string, value: T);

    // ...
}

reader function getNodes(paths: string[]): List<DOMNode> {
    let result = new Dictionary<DOMNode>(); // It is only possible to instantiate this class
                                            // here because it is a 'reader' class.

    for (let path of paths) {

        let node = dom.getNode(path); // Despite the fact that DOM is not 'purely' a 
                                      // 'reader' interface (as it changes the internal state of 
                                      // the browser), 'getNode' is a reader, so it is guranteed 
                                      // not to modify any state, including the state of any 
                                      // external class that may lie 'beneath' its interface

        result.add(path, node); // The 'add' operation is destructive, however, since 'Dictionary' 
                                // is a 'reader' class the effect would be only local to this
                                // function, so this is allowed.
    }

    return result;
} 

Open questions

  • Since interface properties, (i.e. not methods) can have getters behind them, and these getters technically may have side effects, should they be required to be marked as readers as well?
  • Can the 'reader' trait be detected implicitly? This would be very important for anonymous functions and object literals (or even classes), in order for them to be easily passed to reader functions or methods.
  • If an instance of a reader class is passed to a reader function, should the function be allowed to call the non-reader methods of that class? (doing that could indirectly mutate external state, but only through that particular class instance)
  • What about a more precise annotation for a member of a non-reader class that can only mutate its own class instance?
  • What about functions declared inside of a reader function, can they be non-readers but still bound by the scope of the enclosing function, just like in classes?
  • What about the returned values of a reader function? should they be 'reduced' to their reader members as well? maybe this would happen implicitly but only inside of a reader scope?
  • Since objects are assigned by reference, how would an object be read from an argument or a closure for the purpose of non-destructive modifications by using a copy? some form of cloning?
  • Any better names than reader? or maybe a different name for the class modifier?
  • Millions of other small or subtle issues.. :)

Since interface properties, (i.e. not methods) can have getters behind them, and these getters technically may have side effects, should they be required to be marked as readers as well?

I hadn't considered this.
https://github.com/jonaskello/tslint-immutable/issues/41

When will this be implemented?

Proposal draft : the pure modifier

I will suggest using the name pure instead of using reader to have more clarification.
For example :

var mutableVar = 1;
const obj = { a: "hi" };
let func = () => { mutableVar = 3 };

pure function doSomething() {
    mutableVar = 2; // Error
    obj.a = "bye"; // Error
    func(); // Error
}

doSomething();

So the metaphor goes like this :

  • By default, any function in TypeScript maybe dirty (means they may have side effects)
  • But, if a function is marked with pure, then it shall be pure (no side effects) or else the compiler will throw error

Regarding function call

  • To make it easy, all pure function can only call pure function.
  • Calling a function that is not marked pure should be an error even though the function does not bring any side effects.
  • For example:
function add(x: int, y: int){
    return x + y;
}

pure function getMyName(){
    return "Spongebob";
}

pure function doSomething() {
    var x = add(1, 2); // Error
    var s = getMyName(); // No error
}

Regarding I/O operations

  • Since pure function should have no side effects, it is also reasonable that a pure function should not call any I/O functions.
  • Actually, it is also due to the reason that I/O functions are not possibly marked as pure
  • For example :
pure function sayCheese(){
    console.log("Cheese!") //Error
}

Regarding pure classes

* Classes cannot be marked as pure as this violates the whole purpose of object-oriented paradigm, as classes are meant to store state.

  • Classes can be mark as pure (as suggested by @Dessix and @LewisAndrewCampbell), however it must satisfied all of the following characteristics:

    • Every field in the class is readonly

    • Every method is pure

For example, the following is a valid pure class:

pure class Color {
  public readonly red  : number;
  public readonly green: number;
  public readonly blue : number;

  public constructor(red:number, green:number, blue:number) {
    this.red   = red;
    this.green = green;
    this.blue  = blue;
  }

  public pure darker(factor = 0.25): Color {
    return new Color(
      this.red   * factor, 
      this.green * factor,
      this.blue  * factor
    );
  }

}

Regarding pure methods

  • Methods within a class can be marked as pure :
class Example {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    pure sayHello() {        
        this.greeting += "!!!!"; // Error as pure method cannot modify variable outside of own scope
        return this.greeting; // No error
    }
}
  • Constructor cannot be mark as pure.
  • pure methods behave just like pure function, they can only call other function or method that is mark pure and vice versa.
  • For example :
class Starfish {
    name: string;
    constructor(name: string) {
        this.name = name;
    }

    shout() {
        return "Hey I'm Patrick";
    }

    pure jump() {
        return "How can I jump?";
    }      
}

class Sponge {
    friend: StarFish;
    constructor(friend: Patrick){
        this.friend = friend;
    }

    pure play(){
        friend.shout(); // Error because shout() is not pure
        friend.jump(); // No error as jump() is pure
    }
}   
 ```
 * Based on the rule above, a function is also allowed to call a pure method of  a class. 

## Final words
If you have a C++ experience, you'll notice that `pure` is actually the [`const` modifier](http://www.geeksforgeeks.org/const-member-functions-c/). For example, a pure function/method in C++ would be declare as such: 
```cpp
class Example {
    private:
        string name;
    public:
        string GetName() const {
            return name;
        }
}

As a good practice, methods/function should be marked as pure whenever possible so that the process of debugging will be as smooth as jelly.

Suggestion

If this pure modifier is implemented, the TypeScript linter TSLint should suggest coder to mark any function that does not have side effect as pure.

Changelog

  • Added description for pure class. (2018-05-15)

Why clean instead of pure? Pure seems to be the accepted jargon in functional programming for having no side effects.

Other than that it's looking nice

@lastmjs Changed to pure already.

@wongjiahau
Immutable objets (strings, numbers, ...) and then "pure" classes make sense...

@wongjiahau

I love your proposal, but I do have a serious issue with this:

Classes cannot be marked as pure as this violates the whole purpose of object-oriented paradigm, as classes are meant to store state.

It's fairly contentious to say that's the entire point of OO. I mean there are multiple definitions, it's not like there's a set of commandments saying objects are this, or objects are that. More concretely - immutable objects are a very common design pattern. If every field in the class is readonly and every method is pure, it makes sense to me to say that the object is pure.

@wongjiahau In agreement with @LewisAndrewCampbell, Scala serves as an example, as it has its record types (known as "Case classes"), which are often treated as immutable (and, often, pure).
As with most languages' record types, to "change" these objects, you use a special syntactic construct to clone them while altering only the properties you explicitly specify.
As for purity opposing OO- Scala is probably one of the more strict OO languages I've seen- it doesn't include static for example, as even the static instance is a class (referred to as a "Companion Object"). Many developers use the language as if purity were enforced- and the language team is in the midst of designing an "Effects System" which allows enforced purity.

@LewisAndrewCampbell @Dessix I had updated the proposal based on your comments.

If TS can detect error in pure function, it can infer state on function, pure or not. So, I think pure modified could be avoided.

@Strate explicit modifiers like pure is what holds the code from falling apart

imagine you chose to deduce them automatically, and in an ideal situation everything compiles...
then you broke something that rendered one function impure... and got 4236 cascading errors because of one place that is not pure anymore, now go figure what went wrong, i guarantee you will be looking for a needle in a stack of hay

instead if you chose to go with explicit modifiers the error would be localized within a few files

Is this a dupe of #3882?

@isiahmeadows I would say this is an incarnation of #3882 as this threads had more ideas and activities.

Just came upon this topic because I liked the way that D implements pure functions and would love to have similar abilities in JavaScript. Does anyone know if this topic is still discussed for TypeScript? Or how to best progress to get it on the roadmap?

Any movement on this? It fits nicely with TypeScript's design goal of leaving no footprints on runtime code, unlike immutable.js, the current standard. Preventing side-effects statically would be a minor revolution for the JavaScript ecosystem I think.

What about const keyword in place of pure? pure is a valid identifier in JavaScript while const isn't.

const already has a meaning in JS and TS, so that would break it. pure is a valid JS identifier, but so are number, string, etc. TS necessarily has to reserve more keywords than JS to do its job.

Perhaps instead of a fix modifier something that allow us to control what can be done inside a function, it will allow more things like a function that can run only some small set of functions.

Or at least some "official statement" would be cool, even if is "if someone is willing to contribute feel free"

Any news on this issue? Would love to have this kind of types in TypeScript!

Are there any news? This is one of those things that could really improve the code quality in bigger projects.

Any news?

Was this page helpful?
0 / 5 - 0 ratings