{"action":"created","issue":{"url":"https://api.github.com/repos/HaxeFoundation/haxe/issues/4281","labels_url":"https://api.github.com/repos/HaxeFoundation/haxe/issues/4281/labels{/name}","comments_url":"https://api.github.com/repos/HaxeFoundation/haxe/issues/4281/comments","events_url":"https://api.github.com/repos/HaxeFoundation/haxe/issues/4281/events","html_url":"https://github.com/HaxeFoundation/haxe/issues/4281","id":84153343,"number":4281,"title":"Concise notation for refining abstract union types to concrete types","user":{"login":"knuton","id":47458,"avatar_url":"https://avatars.githubusercontent.com/u/47458?v=3","gravatar_id":"","url":"https://api.github.com/users/knuton","html_url":"https://github.com/knuton","followers_url":"https://api.github.com/users/knuton/followers","following_url":"https://api.github.com/users/knuton/following{/other_user}","gists_url":"https://api.github.com/users/knuton/gists{/gist_id}","starred_url":"https://api.github.com/users/knuton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/knuton/subscriptions","organizations_url":"https://api.github.com/users/knuton/orgs","repos_url":"https://api.github.com/users/knuton/repos","events_url":"https://api.github.com/users/knuton/events{/privacy}","received_events_url":"https://api.github.com/users/knuton/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/HaxeFoundation/haxe/labels/haxe-foundation-partner","name":"haxe-foundation-partner","color":"fbca04"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":5,"created_at":"2015-06-02T18:51:35Z","updated_at":"2015-06-03T10:22:51Z","closed_at":null,"body":"The standard library has an [`EitherType`](https://github.com/HaxeFoundation/haxe/blob/development/std/haxe/extern/EitherType.hx) (see also HaxeFoundation/hxnodejs#15) to facilitate working with extern functions that accept either of two types in an argument position. But this is only for calling from Haxe into the outside world.\r\n\r\nComing the other way, calling from the outside world into Haxe, we face the difficulty of not being able to safely pass from arbitrary `EitherType`-style type unions back to concrete types in our Haxe code.\r\n\r\nA member of a union type is a member of one of several concrete types, so it would be great if we could use pattern matching to safely transition to the concrete type of a value. For example with a value `v` of some union type `U = A | B | C`, we could write\r\n\r\n```haxe\r\n// v : U\r\nswitch (v) {\r\n    case A(vA): // vA : A\r\n    case B(vB): // vB : B\r\n    case C(vC): // vC : C\r\n}\r\n```\r\n\r\nThis idea derives from [Scala's extractor pattern](http://lampwww.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf) where it is even possible to \"extract\" values from the value being matched on (this part is akin to Haxe's extractors).\r\n\r\nThe best solution using current language features seems to be using Haxe extractors. Assume concrete types `FruitItem` and `BeverageItem` with union `GroceryItem`:\r\n\r\n```haxe\r\ntypedef FruitItem = { tag: ItemTag, name: String, count: Int };\r\ntypedef BeverageItem = { tag: ItemTag, name: String, brand: String, liters: Float };\r\nabstract GroceryItem(Dynamic) from FruitItem to FruitItem from BeverageItem to BeverageItem {}\r\n```\r\n\r\nWith `item : GroceryItem` we can do\r\n\r\n```haxe\r\nswitch (item) {\r\n\tcase Fruit.unapply(_) => Some(fruitItem): // fruitItem : FruitItem\r\n\tcase Beverage.unapply(_) => Some(beverageItem): // beverageItem : BeverageItem\r\n\tcase _:\r\n}\r\n```\r\n\r\nwhere `unapply` works by runtime inspection of the underlying value. The benefit is that the unsafe access is encapsulated in one operation which allows us to go back to type safety. See [@Simn's Gist](https://gist.github.com/Simn/321a0214e9fac687adca) for details.\r\n\r\nIt would be great if it was possible to improve this to read more like\r\n\r\n```haxe\r\nswitch (item) {\r\n\tcase Fruit(fruitItem): // fruitItem : FruitItem\r\n\tcase Beverage(beverageItem): // beverageItem : BeverageItem\r\n\tcase _:\r\n}\r\n```\r\n\r\n@Simn had some ideas on how this might be done (as well as reservations about whether it should be done). I'll omit them for the moment.\r\n\r\nI give a brief summary of our original motivation to allow further backtracking, but the above is a general approach to concisely express operations on data based on some interesting properties. (For example, it subsumes [@nadako's example for operating on JSON data in arbitrary languages](https://github.com/HaxeFoundation/haxe/issues/4264#issuecomment-107720274).)\r\n\r\n### Background\r\n\r\nWe use a variety of compile-to-JS languages in a modular codebase, with [an interface definition language](https://github.com/prezi/spaghetti) serving to allow communication between modules compiled from different source languages, while holding up some type-safety guarantees. We are currently evaluating possibilities for adding variant type support to our cross-module interfaces, where the goal is for any \"reasonably advanced\" language to be able to work with values of variant types in some form.\r\n\r\nThe natural expression of variant types in a number of languages, including Haxe, are algebraic data types (\"enums\" in Haxe). However, the underlying representation of algebraic data types is opaque (this is a feature of ADTs), and in effect representations differ between programming languages -- we can not rely on any one native representation if we want to directly exchange data between modules.\r\n\r\nThis means that we must find a representation that is general enough to be usable in a variety of languages, while affording (some) safety guarantees by their type systems.\r\n\r\nAs ultimately we are passing around JavaScript values, tagged objects seem to be the most reasonable general representation. You probably understand what I mean already, but as a quick example, let's consider a shopping list.\r\n\r\n```js\r\nvar toBuy = [\r\n  { type: \"fruit\", name: \"Apple\", count: 5 },\r\n  { type: \"fruit\", name: \"Orange\", count: 3 },\r\n  { type: \"beverage\", name: \"Sprite\", brand: \"Coca-Cola\", liters: 2.5 }\r\n];\r\n```\r\n\r\nLet's assume that we only ever need fruits and beverages, so the task is to find a type for shopping list items such that only objects of the two shapes above are permitted and such that we can tell for any value which shape we are facing in order to access its members.\r\n\r\nNote that `{ type: \"immaterial\", name: \"Liberty\" }` is not permitted."},"comment":{"url":"https://api.github.com/repos/HaxeFoundation/haxe/issues/comments/108289789","html_url":"https://github.com/HaxeFoundation/haxe/issues/4281#issuecomment-108289789","issue_url":"https://api.github.com/repos/HaxeFoundation/haxe/issues/4281","id":108289789,"user":{"login":"nadako","id":49749,"avatar_url":"https://avatars.githubusercontent.com/u/49749?v=3","gravatar_id":"","url":"https://api.github.com/users/nadako","html_url":"https://github.com/nadako","followers_url":"https://api.github.com/users/nadako/followers","following_url":"https://api.github.com/users/nadako/following{/other_user}","gists_url":"https://api.github.com/users/nadako/gists{/gist_id}","starred_url":"https://api.github.com/users/nadako/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nadako/subscriptions","organizations_url":"https://api.github.com/users/nadako/orgs","repos_url":"https://api.github.com/users/nadako/repos","events_url":"https://api.github.com/users/nadako/events{/privacy}","received_events_url":"https://api.github.com/users/nadako/received_events","type":"User","site_admin":false},"created_at":"2015-06-03T10:22:51Z","updated_at":"2015-06-03T10:22:51Z","body":"Okay, how about doing the reverse - using an proper enum, but having a macro class that somehow controls its actual representation? (there are open questions about enum reflection though)"}}