{"action":"created","issue":{"url":"https://api.github.com/repos/masak/007/issues/118","labels_url":"https://api.github.com/repos/masak/007/issues/118/labels{/name}","comments_url":"https://api.github.com/repos/masak/007/issues/118/comments","events_url":"https://api.github.com/repos/masak/007/issues/118/events","html_url":"https://github.com/masak/007/issues/118","id":125031465,"number":118,"title":"Provide a 'mut' slang","user":{"login":"masak","id":20627,"avatar_url":"https://avatars.githubusercontent.com/u/20627?v=3","gravatar_id":"","url":"https://api.github.com/users/masak","html_url":"https://github.com/masak","followers_url":"https://api.github.com/users/masak/followers","following_url":"https://api.github.com/users/masak/following{/other_user}","gists_url":"https://api.github.com/users/masak/gists{/gist_id}","starred_url":"https://api.github.com/users/masak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/masak/subscriptions","organizations_url":"https://api.github.com/users/masak/orgs","repos_url":"https://api.github.com/users/masak/repos","events_url":"https://api.github.com/users/masak/events{/privacy}","received_events_url":"https://api.github.com/users/masak/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2016-01-05T19:15:51Z","updated_at":"2016-01-10T11:14:38Z","closed_at":null,"body":"Let's define an *index path* as a variable (a *root*), followed by one or more\r\nindex operations:\r\n\r\n    x                   # index path\r\n    y[7]                # index path\r\n    z.foo[0][\"bar\"]     # index all the way\r\n    w()                 # nope\r\n    n + 1               # nah\r\n\r\nThe idea is to capture the notion of \"lvalue\" in 007: something that we are\r\nable to modify, if only by updating the value in the root.\r\n\r\nWe could imagine a convenience function to help do such updates. We can't\r\nwrite `a[0][1][2] = \"foo\";` in 007, because updates only happen on variables.\r\nBut we could write something like\r\n\r\n    a = mutate(a, sub (context) {\r\n        context.set(0, sub (value) {\r\n            return mutate(value, sub (context) {\r\n                context.set(1, sub (value) {\r\n                    return mutate(value, sub (context) {\r\n                        context.set(2, \"foo\");\r\n                    });\r\n                });\r\n            });\r\n        });\r\n    });\r\n\r\nBasically, each indexing operation translates to a call to `context.set`, until\r\nwe bottom out in a primitive value.\r\n\r\nThis is straightforward, and not too hard to implement, but definitely no fun\r\nto write. So let's introduce a slang to make it more palatable:\r\n\r\n    a = mut {\r\n        _[0][1][2] = \"foo\";\r\n    };\r\n\r\nThis desugars to the above nested function expression. The `_` variable is\r\nforcibly introduced into the `mut` block context without any explicit\r\ndeclaration. Inside a `mut` block, the left-hand side of assignments can\r\nbe any index path of `_`, suppressing the normal requirement in 007 that it\r\nbe a simple identifier.\r\n\r\nAs a special case, we could have\r\n\r\n    n = mut {\r\n        _ = _ + 1;\r\n    };\r\n\r\nDesugar to just `n = n + 1`. Simply because that's what the user would expect.\r\n\r\nAllowing several assignments inside a `mut` shouldn't pose any problems. The\r\nupdate would still be transactional, that is, the whole structure gets replaced\r\nall at once. The final result should be exactly as if mutation of index paths\r\nwere possible in the main language. Assignments should be evaluated in order.\r\nFor example:\r\n\r\n    a = [0, 1, 2];\r\n    a = mut {\r\n        _[2] = _[0];\r\n        _[0] = \"zow!\";\r\n    };\r\n\r\nShould assign `[\"zow!\", 1, 0]` to `a`, and not `[\"zow!\", 1, \"zow!\"]`.\r\n\r\nIt should also be possible to call `.push`, `.pop` (and optionally `.shift` and\r\n`.unshift`) on arrays.\r\n\r\n    state = mut {\r\n        my disk = _[oldpile].pop();\r\n        _[newpile].push(disk);\r\n    };\r\n\r\nEven with these operations, assignments happen in order. For example, an index\r\noperations doesn't commute with a `.shift` operation.\r\n\r\nThere must be an assignment operator (`=`) just to the left of the `mut` macro\r\nkeyword; otherwise, the macro dies in agony.\r\n\r\nBecause this assignment is itself an expression, there's also a method to\r\ncontrol the value of this expression:\r\n\r\n    n = mut {\r\n        _.yield(_);\r\n        _ = _ + 1;\r\n    };\r\n\r\n(This example emulates the `prefix:<++>` operator found in many languages.) The\r\n`.yield` call can appear anywhere in the block, but it's illegal to have more\r\nthan one. If no `.yield` call is present, the resulting value that gets\r\nassigned back to the variable is also the value of the whole assignment\r\nexpression.\r\n\r\nIn fact, we could easily provide both `prefix:<++>` and `postfix:<++>` and\r\ntheir corresponsing `--` forms as macros that make use of the `mut` slang. So\r\nthose would work seamlessly not only on variables, but on any index path.\r\n\r\nWe could take this further and supply `+=`, `*=` et al., with the usual\r\nsemantics. This would probably tempt us to toy around with metaoperators in\r\n007, since one would expect a new infix operator `⊚` to get a corresponding\r\n`⊚=` form automatically.\r\n\r\nI'm not too worried about that. But I am a bit concerned that good ol'\r\nassignment (`=`) will seem a bit underpowered next to all the sugary macro-y\r\noperators, and that people will attempt to assign to index paths outside of a\r\n`mut`, get the compilation error, and curse 007 for being inconsistent."},"comment":{"url":"https://api.github.com/repos/masak/007/issues/comments/170335375","html_url":"https://github.com/masak/007/issues/118#issuecomment-170335375","issue_url":"https://api.github.com/repos/masak/007/issues/118","id":170335375,"user":{"login":"masak","id":20627,"avatar_url":"https://avatars.githubusercontent.com/u/20627?v=3","gravatar_id":"","url":"https://api.github.com/users/masak","html_url":"https://github.com/masak","followers_url":"https://api.github.com/users/masak/followers","following_url":"https://api.github.com/users/masak/following{/other_user}","gists_url":"https://api.github.com/users/masak/gists{/gist_id}","starred_url":"https://api.github.com/users/masak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/masak/subscriptions","organizations_url":"https://api.github.com/users/masak/orgs","repos_url":"https://api.github.com/users/masak/repos","events_url":"https://api.github.com/users/masak/events{/privacy}","received_events_url":"https://api.github.com/users/masak/received_events","type":"User","site_admin":false},"created_at":"2016-01-10T11:14:38Z","updated_at":"2016-01-10T11:14:38Z","body":"Here, for what it's worth, is a diff showing how the hanoi.007 example would look under the `mut` slang:\r\n\r\n```diff\r\ndiff --git a/examples/hanoi.007 b/examples/hanoi.007\r\nindex 0a7aaae..cc1885f 100644\r\n--- a/examples/hanoi.007\r\n+++ b/examples/hanoi.007\r\n@@ -13,54 +13,6 @@ my state = [\r\n     []\r\n ];\r\n \r\n-sub upto(n) {\r\n-    my c = -1;\r\n-    sub counter(irrelevant_I) {\r\n-        c = c + 1;\r\n-        return c;\r\n-    }\r\n-\r\n-    return map(counter, [1] xx n);\r\n-}\r\n-\r\n-sub pop(n) {\r\n-    sub pop_from_nth_pile(nth) {\r\n-        my pile = state[nth];\r\n-        if n != nth {\r\n-            return pile;\r\n-        }\r\n-\r\n-        sub pile_item(i) { return pile[i] }\r\n-\r\n-        return map(pile_item, upto(elems(pile) - 1));\r\n-    }\r\n-\r\n-    my L = elems(state[n]) - 1;\r\n-    my disk = state[n][L];\r\n-    state = map(pop_from_nth_pile, upto(3));\r\n-    return disk;\r\n-}\r\n-\r\n-sub push(disk, n) {\r\n-    sub push_to_nth_pile(nth) {\r\n-        my pile = state[nth];\r\n-        if n != nth {\r\n-            return pile;\r\n-        }\r\n-\r\n-        sub pile_item(i) {\r\n-            if i == elems(pile) {\r\n-                return disk;\r\n-            }\r\n-            return pile[i];\r\n-        }\r\n-\r\n-        return map(pile_item, upto(elems(pile) + 1));\r\n-    }\r\n-\r\n-    state = map(push_to_nth_pile, upto(3));\r\n-}\r\n-\r\n sub tw(n, L) {\r\n     my e = elems(state[n]);\r\n     if L >= e {\r\n@@ -70,7 +22,7 @@ sub tw(n, L) {\r\n }\r\n \r\n sub show() {\r\n-    for reversed(upto(5)) -> L {\r\n+    for [4, 3, 2, 1, 0] -> L {\r\n         say(tw(0, L) ~ \" \" ~ tw(1, L) ~ \" \" ~ tw(2, L));\r\n     }\r\n }\r\n@@ -79,8 +31,10 @@ sub move(diskname, from, to) {\r\n     say(\"\");\r\n     say(\"Moving \" ~ diskname ~ \" from pile \" ~ str(from + 1) ~ \" to pile \" ~ str(to + 1) ~ \"...\");\r\n     say(\"\");\r\n-    my disk = pop(from);\r\n-    push(disk, to);\r\n+    state = mut {\r\n+        my disk = _[from].pop();\r\n+        _[to].push(disk);\r\n+    };\r\n     show();\r\n }\r\n ```"}}