{"action":"created","comment":{"url":"https://api.github.com/repos/runrev/livecode/pulls/comments/25004493","id":25004493,"diff_hunk":"@@ -0,0 +1,364 @@\n+# Traits in LiveCode Builder\n+Copyright 2015 LiveCode Ltd.\n+\n+## Introduction\n+\n+There's a need in Builder to be able to define operations that are specialized\n+by type.\n+\n+Traits allow methods to be \"added on\" to other types and allow in turn allow\n+those other types to be used anywhere where the trait may be used.\n+\n+## Background: the string formatting problem\n+\n+An example of this requirement is the `formatted as string` syntax.  Ideally,\n+a value of any type `T` should be able to be used with the `formatted as\n+string` syntax, e.g.:\n+\n+    variable tVar as T\n+    variable tString as string\n+    put tVar formatted as string into tString\n+\n+### Polymorphic syntax is insufficient\n+\n+Since Builder has polymorphic syntax, it would be legitimate to write a syntax\n+definition and associated handler of the form:\n+\n+    syntax TFormattedAsString is postfix operator with precedence 1\n+        <Operand: Expression> \"formatted\" \"as\" \"string\"\n+    begin\n+        TFormatAsString(Operand, output)\n+    end syntax\n+\n+    public handler TFormatAsString(in pValue as T, out rFormat as string)\n+        -- Perform formatting on T\n+    end handler\n+\n+There are significant drawbacks to this approach.\n+\n+1. Syntax definition has a low level of accessibility.  It's often not clear\n+   what precedence a given syntax definition should be given, and what the\n+   implications of the various syntax classes are.\n+\n+2. In the current Builder implementation, syntax must be built into the\n+   compiler.  Hooking in additional syntax requires a large number of\n+   modifications around the LiveCode build system, and a full recompilation of\n+   the whole of LiveCode.  This provides an extremely high barrier to entry if\n+   all that's wanted is to add a type specialization to a syntax that is\n+   already standard.\n+\n+3. The only \"interesting\" part of the specialization is the handler which\n+   performs the formatting.  The rest of the definition is redundant.\n+\n+### Generic handlers are insufficient\n+\n+It has previously been suggested that Builder should have generic handlers.\n+For example, the standard library could contain a generic handler declaration:\n+\n+    module S\n+\n+    public generic handler FormatAsString(in pOperand as generic, \\\n+                                          out rFormat as string)\n+\n+Then, another module could define a formattable type:\n+\n+    module M\n+    use S\n+    public type T -- Some full declaration\n+\n+    public handler FormatAsString(in pOperand as T, out rFormat as string)\n+        -- Perform formatting on T\n+    end handler\n+\n+In this case, the compiler would identify that `M.FormatAsString<T>()` has a\n+name that matches a generic handler declaration, and that it conforms (all\n+generic parameters are specialised and all non-generic parameters are of the\n+correct types).  Subsequent calls to `FormatAsString()` from *anywhere in the\n+program* would then be able to access `M.FormatAsString<T>()` if the first\n+argument of `FormatAsString()` conforms to `T`.\n+\n+The main drawback here is that Builder is optionally-typed, but it would be\n+good to ensure that it's possible to write code where all types are specified\n+*and can be verified* at compile time.  This doesn't satisfy the requirement.\n+\n+For example, it would be convenient to write a `Log()` handler that can log\n+any type that can also be formatted.  However, with the generic handlers scheme\n+described above, `Log()` would have to take arguments as `any` and defer\n+type-checking to runtime.\n+\n+## Traits\n+\n+Traits are collections of methods that can be used to extend the functionality\n+of a type.\n+\n+Traits have no storage associated with them.  Storage is provided by\n+implementing the trait for a specific storage type.\n+\n+### Defining traits\n+\n+    public trait type CanFormatAsString\n+        method type FormatAsString(in me) as string\n+    end type\n+\n+A trait definition is a block containing a list of zero or more `method type`\n+declarations.  `method type` declarations take the same form as existing\n+`handler type` declarations.\n+\n+Two additional symbols are defined within a trait definition:\n+\n+1. `MyType` is always equivalent to the type of the value that the method is\n+   being called for.\n+2. `me` must always be the first parameter a `method` definition or\n+   `method type` declaration; it is equivalent to writing `me as MyType`.  The\n+   `me` parameter must always be `in` or `inout`.\n+\n+### Implementing traits\n+\n+    public implementation of CanFormatAsString for T\n+        method FormatAsString(in me) as string\n+            variable tFormatted as string\n+            -- Format me parameter\n+            return tFormatted\n+        end method\n+    end implementation\n+\n+`method` definitions take the same form as existing `handler` definitions.\n+\n+In order for an `implementation of U for T` to be valid, the following criteria\n+must be fulfilled:\n+\n+1. **Completeness Rule**.  All methods of `U` must be implemented (either in\n+   the `implementation` block or via a default implementation in the trait\n+   definition).\n+2. **Locality Rule**.  Either `U` or `T` must be local, i.e. defined in the\n+   current module.\n+3. **Visibility Rule**.  The visibility of the implementation must be at least\n+   the accessibility of `U` (i.e. if `U` is `public`, then the implementation\n+   must also be `public`).\n+\n+#### Completeness rule\n+\n+The **Completeness Rule** is required to ensure that it is possible to call any\n+method of a trait `U` for any value of type `T` for which `U` has been\n+implemented.\n+\n+#### Locality rule\n+\n+The **Locality Rule** prevents two implementations of `T`.  Without this rule,\n+the following scenario would be possible:\n+\n+* Module `A` defines and type `T`\n+* Module `B` implements `CanFormatAsString` for `T`\n+* Module `C` implements `CanFormatAsString` for `T` (differently)\n+* Module `D` uses modules `B` and `C` and attempts to format an instance of `T`\n+  as a string.  Which implementation should be used?\n+\n+By requiring either the trait type or the concrete type to be defined locally\n+to the implementation, this diamond problem can never occur.\n+","path":"docs/specs/lcb-traits.md","position":158,"original_position":158,"commit_id":"b7a07fe4f62e1c874a5902ee67add1898e5f44b4","original_commit_id":"b7a07fe4f62e1c874a5902ee67add1898e5f44b4","user":{"login":"runrevfraser","id":5311346,"avatar_url":"https://avatars.githubusercontent.com/u/5311346?v=3","gravatar_id":"","url":"https://api.github.com/users/runrevfraser","html_url":"https://github.com/runrevfraser","followers_url":"https://api.github.com/users/runrevfraser/followers","following_url":"https://api.github.com/users/runrevfraser/following{/other_user}","gists_url":"https://api.github.com/users/runrevfraser/gists{/gist_id}","starred_url":"https://api.github.com/users/runrevfraser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/runrevfraser/subscriptions","organizations_url":"https://api.github.com/users/runrevfraser/orgs","repos_url":"https://api.github.com/users/runrevfraser/repos","events_url":"https://api.github.com/users/runrevfraser/events{/privacy}","received_events_url":"https://api.github.com/users/runrevfraser/received_events","type":"User","site_admin":false},"body":"The issue is that both Bob and Carol might implement the trait for T. Which implementation gets used when Dave's module tries to format a T as a string?","created_at":"2015-02-19T17:03:01Z","updated_at":"2015-02-19T17:03:01Z","html_url":"https://github.com/runrev/livecode/pull/1864#discussion_r25004493","pull_request_url":"https://api.github.com/repos/runrev/livecode/pulls/1864","_links":{"self":{"href":"https://api.github.com/repos/runrev/livecode/pulls/comments/25004493"},"html":{"href":"https://github.com/runrev/livecode/pull/1864#discussion_r25004493"},"pull_request":{"href":"https://api.github.com/repos/runrev/livecode/pulls/1864"}}},"pull_request":{"url":"https://api.github.com/repos/runrev/livecode/pulls/1864","id":29526612,"html_url":"https://github.com/runrev/livecode/pull/1864","diff_url":"https://github.com/runrev/livecode/pull/1864.diff","patch_url":"https://github.com/runrev/livecode/pull/1864.patch","issue_url":"https://api.github.com/repos/runrev/livecode/issues/1864","number":1864,"state":"open","locked":false,"title":"[RFC] [feature-traits] Adding traits to LiveCode Builder","user":{"login":"peter-b","id":47528,"avatar_url":"https://avatars.githubusercontent.com/u/47528?v=3","gravatar_id":"","url":"https://api.github.com/users/peter-b","html_url":"https://github.com/peter-b","followers_url":"https://api.github.com/users/peter-b/followers","following_url":"https://api.github.com/users/peter-b/following{/other_user}","gists_url":"https://api.github.com/users/peter-b/gists{/gist_id}","starred_url":"https://api.github.com/users/peter-b/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/peter-b/subscriptions","organizations_url":"https://api.github.com/users/peter-b/orgs","repos_url":"https://api.github.com/users/peter-b/repos","events_url":"https://api.github.com/users/peter-b/events{/privacy}","received_events_url":"https://api.github.com/users/peter-b/received_events","type":"User","site_admin":false},"body":"There's a need in Builder to be able to define operations that are specialized by type.\r\n\r\nTraits allow methods to be \"added on\" to other types and allow in turn allow those other types to be used anywhere where the trait may be used.\r\n\r\nTraits provide a reasonably straightforward and clean way to provide extensible standard library syntax without the need for generic handlers and (mostly) preserving the ability to perform compile-time type checking.\r\n\r\n[Rendered version](https://github.com/peter-b/livecode/blob/feature-traits/docs/specs/lcb-traits.md)","created_at":"2015-02-18T13:55:36Z","updated_at":"2015-02-19T17:03:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0a74981785e1289069a11a9b5de8545ec041e573","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/runrev/livecode/pulls/1864/commits","review_comments_url":"https://api.github.com/repos/runrev/livecode/pulls/1864/comments","review_comment_url":"https://api.github.com/repos/runrev/livecode/pulls/comments/{number}","comments_url":"https://api.github.com/repos/runrev/livecode/issues/1864/comments","statuses_url":"https://api.github.com/repos/runrev/livecode/statuses/b7a07fe4f62e1c874a5902ee67add1898e5f44b4","head":{"label":"peter-b:feature-traits","ref":"feature-traits","sha":"b7a07fe4f62e1c874a5902ee67add1898e5f44b4","user":{"login":"peter-b","id":47528,"avatar_url":"https://avatars.githubusercontent.com/u/47528?v=3","gravatar_id":"","url":"https://api.github.com/users/peter-b","html_url":"https://github.com/peter-b","followers_url":"https://api.github.com/users/peter-b/followers","following_url":"https://api.github.com/users/peter-b/following{/other_user}","gists_url":"https://api.github.com/users/peter-b/gists{/gist_id}","starred_url":"https://api.github.com/users/peter-b/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/peter-b/subscriptions","organizations_url":"https://api.github.com/users/peter-b/orgs","repos_url":"https://api.github.com/users/peter-b/repos","events_url":"https://api.github.com/users/peter-b/events{/privacy}","received_events_url":"https://api.github.com/users/peter-b/received_events","type":"User","site_admin":false},"repo":{"id":24674939,"name":"livecode","full_name":"peter-b/livecode","owner":{"login":"peter-b","id":47528,"avatar_url":"https://avatars.githubusercontent.com/u/47528?v=3","gravatar_id":"","url":"https://api.github.com/users/peter-b","html_url":"https://github.com/peter-b","followers_url":"https://api.github.com/users/peter-b/followers","following_url":"https://api.github.com/users/peter-b/following{/other_user}","gists_url":"https://api.github.com/users/peter-b/gists{/gist_id}","starred_url":"https://api.github.com/users/peter-b/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/peter-b/subscriptions","organizations_url":"https://api.github.com/users/peter-b/orgs","repos_url":"https://api.github.com/users/peter-b/repos","events_url":"https://api.github.com/users/peter-b/events{/privacy}","received_events_url":"https://api.github.com/users/peter-b/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/peter-b/livecode","description":"Peter Brett's LiveCode development repository","fork":true,"url":"https://api.github.com/repos/peter-b/livecode","forks_url":"https://api.github.com/repos/peter-b/livecode/forks","keys_url":"https://api.github.com/repos/peter-b/livecode/keys{/key_id}","collaborators_url":"https://api.github.com/repos/peter-b/livecode/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/peter-b/livecode/teams","hooks_url":"https://api.github.com/repos/peter-b/livecode/hooks","issue_events_url":"https://api.github.com/repos/peter-b/livecode/issues/events{/number}","events_url":"https://api.github.com/repos/peter-b/livecode/events","assignees_url":"https://api.github.com/repos/peter-b/livecode/assignees{/user}","branches_url":"https://api.github.com/repos/peter-b/livecode/branches{/branch}","tags_url":"https://api.github.com/repos/peter-b/livecode/tags","blobs_url":"https://api.github.com/repos/peter-b/livecode/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/peter-b/livecode/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/peter-b/livecode/git/refs{/sha}","trees_url":"https://api.github.com/repos/peter-b/livecode/git/trees{/sha}","statuses_url":"https://api.github.com/repos/peter-b/livecode/statuses/{sha}","languages_url":"https://api.github.com/repos/peter-b/livecode/languages","stargazers_url":"https://api.github.com/repos/peter-b/livecode/stargazers","contributors_url":"https://api.github.com/repos/peter-b/livecode/contributors","subscribers_url":"https://api.github.com/repos/peter-b/livecode/subscribers","subscription_url":"https://api.github.com/repos/peter-b/livecode/subscription","commits_url":"https://api.github.com/repos/peter-b/livecode/commits{/sha}","git_commits_url":"https://api.github.com/repos/peter-b/livecode/git/commits{/sha}","comments_url":"https://api.github.com/repos/peter-b/livecode/comments{/number}","issue_comment_url":"https://api.github.com/repos/peter-b/livecode/issues/comments/{number}","contents_url":"https://api.github.com/repos/peter-b/livecode/contents/{+path}","compare_url":"https://api.github.com/repos/peter-b/livecode/compare/{base}...{head}","merges_url":"https://api.github.com/repos/peter-b/livecode/merges","archive_url":"https://api.github.com/repos/peter-b/livecode/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/peter-b/livecode/downloads","issues_url":"https://api.github.com/repos/peter-b/livecode/issues{/number}","pulls_url":"https://api.github.com/repos/peter-b/livecode/pulls{/number}","milestones_url":"https://api.github.com/repos/peter-b/livecode/milestones{/number}","notifications_url":"https://api.github.com/repos/peter-b/livecode/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/peter-b/livecode/labels{/name}","releases_url":"https://api.github.com/repos/peter-b/livecode/releases{/id}","created_at":"2014-10-01T10:49:08Z","updated_at":"2015-02-11T11:34:53Z","pushed_at":"2015-02-19T16:11:04Z","git_url":"git://github.com/peter-b/livecode.git","ssh_url":"git@github.com:peter-b/livecode.git","clone_url":"https://github.com/peter-b/livecode.git","svn_url":"https://github.com/peter-b/livecode","homepage":"http://www.livecode.com/","size":59472,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"runrev:develop","ref":"develop","sha":"e797a78e4bd771c80fd3914078ff5dab1d4fefb2","user":{"login":"runrev","id":3985749,"avatar_url":"https://avatars.githubusercontent.com/u/3985749?v=3","gravatar_id":"","url":"https://api.github.com/users/runrev","html_url":"https://github.com/runrev","followers_url":"https://api.github.com/users/runrev/followers","following_url":"https://api.github.com/users/runrev/following{/other_user}","gists_url":"https://api.github.com/users/runrev/gists{/gist_id}","starred_url":"https://api.github.com/users/runrev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/runrev/subscriptions","organizations_url":"https://api.github.com/users/runrev/orgs","repos_url":"https://api.github.com/users/runrev/repos","events_url":"https://api.github.com/users/runrev/events{/privacy}","received_events_url":"https://api.github.com/users/runrev/received_events","type":"Organization","site_admin":false},"repo":{"id":9334445,"name":"livecode","full_name":"runrev/livecode","owner":{"login":"runrev","id":3985749,"avatar_url":"https://avatars.githubusercontent.com/u/3985749?v=3","gravatar_id":"","url":"https://api.github.com/users/runrev","html_url":"https://github.com/runrev","followers_url":"https://api.github.com/users/runrev/followers","following_url":"https://api.github.com/users/runrev/following{/other_user}","gists_url":"https://api.github.com/users/runrev/gists{/gist_id}","starred_url":"https://api.github.com/users/runrev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/runrev/subscriptions","organizations_url":"https://api.github.com/users/runrev/orgs","repos_url":"https://api.github.com/users/runrev/repos","events_url":"https://api.github.com/users/runrev/events{/privacy}","received_events_url":"https://api.github.com/users/runrev/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/runrev/livecode","description":"","fork":false,"url":"https://api.github.com/repos/runrev/livecode","forks_url":"https://api.github.com/repos/runrev/livecode/forks","keys_url":"https://api.github.com/repos/runrev/livecode/keys{/key_id}","collaborators_url":"https://api.github.com/repos/runrev/livecode/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/runrev/livecode/teams","hooks_url":"https://api.github.com/repos/runrev/livecode/hooks","issue_events_url":"https://api.github.com/repos/runrev/livecode/issues/events{/number}","events_url":"https://api.github.com/repos/runrev/livecode/events","assignees_url":"https://api.github.com/repos/runrev/livecode/assignees{/user}","branches_url":"https://api.github.com/repos/runrev/livecode/branches{/branch}","tags_url":"https://api.github.com/repos/runrev/livecode/tags","blobs_url":"https://api.github.com/repos/runrev/livecode/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/runrev/livecode/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/runrev/livecode/git/refs{/sha}","trees_url":"https://api.github.com/repos/runrev/livecode/git/trees{/sha}","statuses_url":"https://api.github.com/repos/runrev/livecode/statuses/{sha}","languages_url":"https://api.github.com/repos/runrev/livecode/languages","stargazers_url":"https://api.github.com/repos/runrev/livecode/stargazers","contributors_url":"https://api.github.com/repos/runrev/livecode/contributors","subscribers_url":"https://api.github.com/repos/runrev/livecode/subscribers","subscription_url":"https://api.github.com/repos/runrev/livecode/subscription","commits_url":"https://api.github.com/repos/runrev/livecode/commits{/sha}","git_commits_url":"https://api.github.com/repos/runrev/livecode/git/commits{/sha}","comments_url":"https://api.github.com/repos/runrev/livecode/comments{/number}","issue_comment_url":"https://api.github.com/repos/runrev/livecode/issues/comments/{number}","contents_url":"https://api.github.com/repos/runrev/livecode/contents/{+path}","compare_url":"https://api.github.com/repos/runrev/livecode/compare/{base}...{head}","merges_url":"https://api.github.com/repos/runrev/livecode/merges","archive_url":"https://api.github.com/repos/runrev/livecode/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/runrev/livecode/downloads","issues_url":"https://api.github.com/repos/runrev/livecode/issues{/number}","pulls_url":"https://api.github.com/repos/runrev/livecode/pulls{/number}","milestones_url":"https://api.github.com/repos/runrev/livecode/milestones{/number}","notifications_url":"https://api.github.com/repos/runrev/livecode/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/runrev/livecode/labels{/name}","releases_url":"https://api.github.com/repos/runrev/livecode/releases{/id}","created_at":"2013-04-10T00:15:57Z","updated_at":"2015-02-18T15:00:08Z","pushed_at":"2015-02-19T15:41:32Z","git_url":"git://github.com/runrev/livecode.git","ssh_url":"git@github.com:runrev/livecode.git","clone_url":"https://github.com/runrev/livecode.git","svn_url":"https://github.com/runrev/livecode","homepage":null,"size":121637,"stargazers_count":77,"watchers_count":77,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":68,"mirror_url":null,"open_issues_count":63,"forks":68,"open_issues":63,"watchers":77,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/runrev/livecode/pulls/1864"},"html":{"href":"https://github.com/runrev/livecode/pull/1864"},"issue":{"href":"https://api.github.com/repos/runrev/livecode/issues/1864"},"comments":{"href":"https://api.github.com/repos/runrev/livecode/issues/1864/comments"},"review_comments":{"href":"https://api.github.com/repos/runrev/livecode/pulls/1864/comments"},"review_comment":{"href":"https://api.github.com/repos/runrev/livecode/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/runrev/livecode/pulls/1864/commits"},"statuses":{"href":"https://api.github.com/repos/runrev/livecode/statuses/b7a07fe4f62e1c874a5902ee67add1898e5f44b4"}}}}