5 ms·
You've written a lot, and I can't respond to everything. I'd just like to point out that the Array type is backed by proper JVM arrays. Operations on those are
by herbstein 6y ago
You've written a lot, and I can't respond to everything. I'd just like to point out that the Array type is backed by proper JVM arrays. Operations on those are inherently impure. For a better representation of the effect system on a collection I'd suggest looking at the List type instead.
Source: i worked a bit with the language during Spring.
- tw25513397 6y agoIt just struck me as odd for reads like `find` (which itself requires a pure fn arg). Is that because given the same reference, the function could yield different outputs? Or because it could be mutated concurrently? Would that imply every function that takes a mutable data structure will be impure? Edit: I just noticed that `List.toArray` -- which just returns a new array, so no concern over references or mutation -- is also marked as impure. This seemed wrong to me, but then I noticed that even `Array.new()` is marked impure. To my mind, a function that allocates a new mutable collection is itself not inherently impure.
- lmm 6y ago> Would that imply every function that takes a mutable data structure will be impure? That always has to be true, no? Reading from a mutable datastructure is impure in the same way that reading from standard input is. > Edit: I just noticed that `List.toArray` -- which just returns a new array, so no concern over references or mutation -- is also marked as impure. This seemed wrong to me, but then I noticed that even `Array.new()` is marked impure. To my mind, a function that allocates a new mutable collection is itself not inherently impure. Two different arrays with the same members are not generally equivalent. E.g. (x.toArray, x.toArray) is semantically something very different from {val y = x.toArray; (y, y)}.
- tw25513397 6y ago> That always has to be true, no? Reading from a mutable datastructure is impure in the same way that reading from standard input is. If the function internally reads from stdin, I'd agree. But if a fn takes an input stream as an arg, and if given input streams that yield the same bytes the fn always yields the same result, then why consider it an impure fn? The input stream is just a fancy data structure for bytes. > Two different arrays with the same members are not generally equivalent. Derp, of course! And though I guess it would be possible to have the `Mut*` collections use value equality instead of reference equality, that'd probably conflict with the performance goals of the mutable variant.
- lmm 6y ago> But if a fn takes an input stream as an arg, and if given input streams that yield the same bytes the fn always yields the same result, then why consider it an impure fn? The input stream is just a fancy data structure for bytes. Hmm. It's impossible to create a value that's equivalent to reading from the input stream, because reading from the input stream has operational effects. But that logic doesn't apply to an array if your access to the array is unobservable. (One could argue that reading from an array creates temporal relationships, but I don't think that really holds up). So I think you're right, and a function that accepts a mutable datastructure can still be pure (in a vacuous sense really, since a mutable datastructure can't ever be equivalent to a value - it's not even equivalent to itself at a different time), though given that that purity can't ever be useful to you I don't think it's particularly important. > And though I guess it would be possible to have the `Mut*` collections use value equality instead of reference equality, that'd probably conflict with the performance goals of the mutable variant. It's not about the implementation of .equals(), it's about semantic equivalence. Two different arrays with the same members behave quite differently than two references to the same array (as the program continues and other code mutates them), regardless of whether they compared equal at the start.
- jorkadeen 6y agoWe are still figuring out the details about array impurity. But in a nutshell if something is pure it should support substitution. For example: let x = 123; let y = (x, x) If you substitute x into the pair you get the same result. So far, so good. Now consider: let x = [1, 2, 3]; x[0] = 5; let y = x[0] If you substitute x into the body of y you get a different result! The meaning of x[0] changed underneath you. We are very conservative and say that anything which touches an array is impure. Is this the best solution? no- but it is a reasonable/sound start. (I am one of the authors of Flix).
- tw25513397 6y agoHey, thanks for replying! > But in a nutshell if something is pure it should support substitution. Neat, hadn't thought about it like that before. > If you substitute x into the body of y you get a different result! Heh, I guess that depends on "which" x. I recognize Flix doesn't allow shadowing as a defensive choice, but with respect to substitution, wouldn't this be equivalent to something like: let x = [1, 2, 3] let x = insert(0, 5, x) # treating it like Map [1] let y = get(0, x) IOW, it's not clear to me why the `x[0] = 5` step would be skipped when considering substitution. Hmm, per the substitution principle, is `x[0] = 5` pure? > We are very conservative and say that anything which touches an array is impure. Understandable given how difficult it is to reason about mutable things. ---- [1] Given the uniform function call syntax, I found it odd that the map is the last arg in the function. I originally assumed it would be m.insert(k, v) === insert(m, k, v)