Snipped and backdated from a Reddit comment of mine.
That’s an interesting way to put. It’s almost like const correctness or checked exceptions (done right) for side-effects since you have to maintain purity with what you are calling or otherwise becoming impure.
there might need to be some sort of distinction between a class containing only pure functions and immutable fields, and one containing procedures and/or mutable fields
Yes, const correctness for types, fields, procedures, functions, and everything would be useful to know what can change what.
for a class to be pure it must contain only functions. Similarly, […] a function can contain only expressions, including other functions
Makes sense. Impure code can call what it wants. Pure functions can only call pure functions. The only thing is that if you want a drop of impurity, everything starts becoming impure and it doesn’t convey much of any information.
Even if everything was marked as pure or impure, cost or mutable, we don’t know if an impure procedure is writing to a file, executing a system command, sending a message over sockets, etc. We would want more granularity to know side-effects procedures are causing down the call tree.
Similar to checked exceptions or dependency injection, an idea is that you could have a separate parameter list for resources and side-effects a procedure requires. It’s similar to the ideas in /u/DanielWaterworth’s plastic language or free moands. I also talked about this idea a while back in this comment thread for reference.
Example in some made up syntax:
// `class` or whatever you want to call this
class Cache {
...
// technically `self` would be mutable the way I wrote this
fn get(self, key: String)(effects @fs: FileSystem) {
if (self.contains(key)) {
return self[key]
}
let value = @fs.read(key)
self[key] = value
return value;
}
}
// uses the default, global, "real" file system ("business as usual")
cache.getFromCache("foo")
// uses dummy file system for unit testing
chache.getFromCache("bar")(DummyFileSystem)