Title: [RFC] New Generics · Issue #679 · wurstscript/WurstScript · GitHub
Open Graph Title: [RFC] New Generics · Issue #679 · wurstscript/WurstScript
X Title: [RFC] New Generics · Issue #679 · wurstscript/WurstScript
Description: Currently, generics are translated using erasure. This means, that generics can only be instantiated with types compatible with int. The idea of this proposal is to change the translation and instead of using erasure translation would ge...
Open Graph Description: Currently, generics are translated using erasure. This means, that generics can only be instantiated with types compatible with int. The idea of this proposal is to change the translation and inste...
X Description: Currently, generics are translated using erasure. This means, that generics can only be instantiated with types compatible with int. The idea of this proposal is to change the translation and inste...
Opengraph URL: https://github.com/wurstscript/WurstScript/issues/679
X: @github
Domain: patch-diff.githubusercontent.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"[RFC] New Generics","articleBody":"Currently, generics are translated using erasure. This means, that generics can only be instantiated with types compatible with `int`. The idea of this proposal is to change the translation and instead of using erasure translation would generate one copy per instantiated type. This would allow to extend generics with some interesting new features, but would also be incompatible in some cases.\r\n\r\nLanguage changes:\r\n============\r\n\r\n## 1. Allow all types for generics\r\n\r\n\r\nEvery type can be inserted for a generic type parameter without any restrictions.\r\n\r\nFor example, we can then use `LinkedList\u003cvec3\u003e`.\r\n\r\nThis also allows us to get rid of the implicit calls to `fromIndex` and `toIndex` that we currently have as a woraround.\r\n\r\n## 2. Allow to restrict type parameters / type classes\r\n\r\nSometimes we want to restrict type parameters.\r\nFor example `LinkedList\u003cT\u003e.toString() returns string` only can be implemented, if we have a method `toString(T) returns string`.\r\n\r\nWe could of course provide this method using an additional argument: \r\n\r\n```ceylon\r\nLinkedList\u003cT\u003e.toString(Show\u003cT\u003e elementShower) returns string\r\n ... elementShower.toString(x) ...\r\n\r\ninterface Show\u003cT\u003e\r\n function toString(T elem) returns string\r\n\r\n```\r\n\r\nHowever, it is not very convenient to always pass this `Show`value explicitly. \r\n\r\nInstead we allow to use generic interfaces as type constraints:\r\n\r\n```ceylon\r\nfunction LinkedList\u003cT\u003e.toString\u003cT : Show\u003e() returns string\r\n ... T.toString(x) ...\r\n```\r\n\r\n```ceylon\r\ninterface Serializable\u003cT\u003e\r\n function encode(T t) returns string\r\n function decode(string s) returns T\r\n\r\nfunction LinkedList\u003cT\u003e.encode\u003cT : Serializable\u003e() returns string\r\n ...\r\nfunction LinkedList\u003cT\u003e.decode\u003cT : Serializable\u003e(string s) returns T\r\n ...\r\n```\r\n\r\nThe compiler automatically finds instances that are defined with the new `instance` definitions:\r\n\r\n\r\n```ceylon\r\ninstance Show\u003cvec2\u003e\r\n function toString(vec2 v) returns string\r\n return \"(\" + v.x.toString() + \", \" + v.y.toString() + \")\"\r\n\r\n// make all lists with serializable elements serializable:\r\ninstance \u003cT : Serializable\u003e implements Serializable\u003cLinkedList\u003cT\u003e\u003e\r\n // implement methods here\r\n```\r\n\r\nIf multiple instance are in scope there is a compilation error.\r\n\r\nThe instantiation is part of the type, so the following example is not allowed:\r\n\r\n```ceylon\r\npackage A\r\n\r\npublic interface Ord\u003cT\u003e\r\n function lessEq(T a, T b) returns boolean\r\n\r\ninstance Ord\u003cstring\u003e\r\n function lessEq(string a, string b) returns boolean\r\n return a.length() \u003c= b.length()\r\n\r\npublic class TreeSet\u003cT : Ord\u003e\r\n ...\r\n\r\npublic TreeSet\u003cstring\u003e mySet = TreeSet.create(\"a\",\"aa\",\"aaa\")\r\n\r\npackage B\r\nimport A\r\n\r\ninstance Ord\u003cstring\u003e\r\n function lessEq(string a, string b) returns boolean\r\n return a.length() \u003e= b.length()\r\n\r\ninit\r\n TreeSet\u003cstring\u003e s = mySet // error, because Ord intances are not compatible\r\n\r\n```\r\n\r\nTo resolve instances, we first match the parameters and then match the type constraints from left to right.\r\n\r\n**Type class translation**: The translation uses monomorphization (like traits in Rust) when translating to Jass and dictionary passing when translating to Lua. \r\n\r\n\r\n\r\n## 3. Disallow casting to int\r\n\r\nSince we now allow all types to be used for type parameters, we can no longer cast type parameters to `int`. However, we can use the new feature of restricted type parameters to implement the same feature:\r\n\r\nOld `HashMap`:\r\n\r\n```ceylon\r\npublic class HashMap\u003cK,V\u003e extends Table\r\n\r\n\t/** Whether a value exists under the given key or not */\r\n\tfunction has(K key) returns boolean\r\n\t\treturn hasInt(key castTo int)\r\n\r\n\t/** Saves the given value under the given key */\r\n\tfunction put(K key, V value)\r\n\t\tsaveInt(key castTo int, value castTo int)\r\n\r\n\t/** Retrieves the value saved under the given key */\r\n\tfunction get(K key) returns V\r\n\t\treturn loadInt(key castTo int) castTo V\r\n\r\n\t/** Removes the value saved under the given key */\r\n\tfunction remove(K key)\r\n\t\tremoveInt(key castTo int)\r\n```\r\n\r\nNew `HashMap`\r\n\r\n\r\n\r\n```ceylon\r\ntypeclass Indexable\u003cT\u003e\r\n function toIndex(T elem) returns int\r\n function fromIndex(int index) returns T\r\n\r\npublic class HashMap\u003cK : Indexable, V : Indexable\u003e extends Table\r\n\r\n\t/** Whether a value exists under the given key or not */\r\n\tfunction has(K key) returns boolean\r\n\t\treturn hasInt(K.toIndex(key))\r\n\r\n\t/** Saves the given value under the given key */\r\n\tfunction put(K key, V value)\r\n\t\tsaveInt(K.toIndex(key), V.toIndex(value))\r\n\r\n\t/** Retrieves the value saved under the given key */\r\n\tfunction get(K key) returns V\r\n\t\treturn V.fromIndex(loadInt(K.toIndex(key)))\r\n\r\n\t/** Removes the value saved under the given key */\r\n\tfunction remove(K key)\r\n\t\tremoveInt(K.toIndex(key))\r\n```\r\n\r\nTo simplify the transition, we could automatically interpret the old code as the new one.\r\n\r\nAlso we can automatically create some useful instances for all classes, for example the Indexable above.\r\n\r\nA challenge here is that for Jass and Lua different type classes would make sense: \r\nCasting classes to int in Lua is an ugly and leaky workaround and not really needed as we have tables to store stuff without indexes.\r\n\r\n\r\n## 4. Disallowing casting between different instantiations\r\n\r\nCurrently the following code is valid:\r\n\r\n```ceylon\r\nclass A\r\nclass B extends A\r\n\r\nfunction foo(LinkedList\u003cA\u003e l)\r\n\r\n@Test function testCast()\r\n\tLinkedList\u003cB\u003e list = asList(new B, new B, new B)\r\n\tfoo(list castTo int castTo LinkedList\u003cA\u003e)\r\n```\r\n\r\nWith the proposed changes this would still compile, but might no longer work, since translation might create complete different code for `List\u003cA\u003e` and `List\u003cB\u003e`. \r\nMaybe it makes sense to guarantee that all instantiations with class types share the same code (same as it is now), but this is difficult to implement in combination with typeclasses.\r\n\r\n\r\n","author":{"url":"https://github.com/peq","@type":"Person","name":"peq"},"datePublished":"2018-06-21T19:55:30.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":10},"url":"https://github.com/679/WurstScript/issues/679"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:4d694307-8e70-3175-a5a0-2a05c902c10d |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 8A80:3556CB:6787058:8AEEB72:698CB038 |
| html-safe-nonce | 4f750d3b8bdbaaa1d09ae667b5d5efcd74d07ee8cc10fab9897dfdfe97614fbe |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4QTgwOjM1NTZDQjo2Nzg3MDU4OjhBRUVCNzI6Njk4Q0IwMzgiLCJ2aXNpdG9yX2lkIjoiNjA0MTEzOTI2Nzc5MzUxMDk2IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 0ed17e1e1ada56bfb17c36ce47d4f3b92d8fa41f63773e979cb34d7502e1499e |
| hovercard-subject-tag | issue:334634752 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/wurstscript/WurstScript/679/issue_layout |
| twitter:image | https://opengraph.githubassets.com/34e14a24db723e9d0a4a6789933a4e7f9627a3e567f3183cc1ef37647fd91fb8/wurstscript/WurstScript/issues/679 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/34e14a24db723e9d0a4a6789933a4e7f9627a3e567f3183cc1ef37647fd91fb8/wurstscript/WurstScript/issues/679 |
| og:image:alt | Currently, generics are translated using erasure. This means, that generics can only be instantiated with types compatible with int. The idea of this proposal is to change the translation and inste... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | peq |
| hostname | github.com |
| expected-hostname | github.com |
| None | 640eeb7b6ff4d8d106235d228c0c286e82592d4d2403227b5b2b4fc5832297a4 |
| turbo-cache-control | no-preview |
| go-import | github.com/wurstscript/WurstScript git https://github.com/wurstscript/WurstScript.git |
| octolytics-dimension-user_id | 30814797 |
| octolytics-dimension-user_login | wurstscript |
| octolytics-dimension-repository_id | 3584229 |
| octolytics-dimension-repository_nwo | wurstscript/WurstScript |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 3584229 |
| octolytics-dimension-repository_network_root_nwo | wurstscript/WurstScript |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 3d444f0a47beeeac94cddbb51c91ab408befe8d4 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width