{"action":"reopened","issue":{"url":"https://api.github.com/repos/fsharp/fslang-suggestions/issues/656","repository_url":"https://api.github.com/repos/fsharp/fslang-suggestions","labels_url":"https://api.github.com/repos/fsharp/fslang-suggestions/issues/656/labels{/name}","comments_url":"https://api.github.com/repos/fsharp/fslang-suggestions/issues/656/comments","events_url":"https://api.github.com/repos/fsharp/fslang-suggestions/issues/656/events","html_url":"https://github.com/fsharp/fslang-suggestions/issues/656","id":311725901,"number":656,"title":"Unions as Better Enumerations","user":{"login":"jbeeko","id":4968704,"avatar_url":"https://avatars2.githubusercontent.com/u/4968704?v=4","gravatar_id":"","url":"https://api.github.com/users/jbeeko","html_url":"https://github.com/jbeeko","followers_url":"https://api.github.com/users/jbeeko/followers","following_url":"https://api.github.com/users/jbeeko/following{/other_user}","gists_url":"https://api.github.com/users/jbeeko/gists{/gist_id}","starred_url":"https://api.github.com/users/jbeeko/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jbeeko/subscriptions","organizations_url":"https://api.github.com/users/jbeeko/orgs","repos_url":"https://api.github.com/users/jbeeko/repos","events_url":"https://api.github.com/users/jbeeko/events{/privacy}","received_events_url":"https://api.github.com/users/jbeeko/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-04-05T18:45:44Z","updated_at":"2018-04-05T19:45:55Z","closed_at":null,"author_association":"NONE","body":"## Introduction\r\nThis language suggestion proposes adding Enumerate Unions. These are union types were rather than having different cases each of which defines any number of items the union is an enumeration of the same data for each case. \r\n\r\nOne example domain currencies. There is a fixed list of these and a handful data elements are associated with each currenty:\r\n* name\r\n* number\r\n* minor units\r\n\r\n## Syntax\r\nA proposed syntax for EnumerateUnions is: \r\n\r\n```\r\ntype Currency of Name: string; Number: int =\r\n| CAD Name = \"CAD\"; Number = 123\r\n| USD Name = \"USD\"; Number = 223\r\n| GBP Name = \"GBP\"; Number = 444\r\n\r\nprintfn \"The number of currency: '%s' is: %i\" CAD.Name CAD.Number\r\n```\r\n\r\nThis is very susinct (4 lines vs about 16 using the current languate), easy to understand, models all the data and prevents the construction of invalid currencies.\r\n\r\nHere is a more complex example modeling ISO currency https://en.wikipedia.org/wiki/ISO_4217 and country codes https://en.wikipedia.org/wiki/ISO_3166-1. \r\n\r\n```\r\ntype Country of Name: string; Code: string; LongCode: string; IsoNumber: int; Independant: bool; Currencies : Currency list =\r\n| CA Name = \"Canada\"; Code = \"CA\"; LongCode = \"CAN\"; Number = 124; Independant = true; Currencies = [CAD]\r\n| GB Name = \"United Kingdom of Great Britain and Northern Ireland\"; Code = \"GB\"; LongCode = \"GBR\"; Number = 826; Independant = true; Currencies = [CAD]\r\n| US Name = \"United States of America\"; Code = \"US\"; LongCode = \"USA\"; Number = 840; Independant = true; Currencies = [USD]\r\n| VG Name = \"British Virgin Islands\"; Code = \"VG\"; LongCode = \"VGB\"; Number = 92; Independant = false; Currencies = [USD]\r\n| IO Name = \"British Indian Ocean Teritory\"; Code = \"IO\"; LongCode = \"IOT\"; Number = 86; Independant = false; Currencies = [USD; GBP]\r\n\r\nand Currency of Name: string; Number: int; Digits: int UsedIn: Country list =\r\n| CAD Code = \"CAD\" Name = \"Canadian Dollar\"; Number = 124; Digits = 2; UsedIn = [CA]\r\n| USD Code = \"USD\" Name = \"United States Dollar\"; Number = 840; Digits = 2; UsedIn = [US; VG; IO]\r\n| GBP Code = \"GBP\" Name = \"Pound sterling\"; Number = 826; Digits = 2; UsedIn = [GB; IO]\r\n\r\nprintfn \"The currency: '%s' is used in: %A\" USD.Name USD.UsedIn\r\nprintfn \"The Country: '%s' uses the currencies: %A\" IO.Name IO.Currencies\r\n```\r\n\r\nAgain a lot of information is represented very clearly and compactly in a typesafe way. \r\n\r\n## Uses\r\nWhere would this be useful? All cases where there domains contain an infrequently changing enumeration of items with richer data than just strings or integers. These may be globally defined lists (currencies) or specific to the application. Examples are:\r\n * Chemical elements (possibly including isotopes or isomers)\r\n * Countries\r\n * Other geographic elements such as mountains, states or cities\r\n * Currencies\r\n * BloodTypes\r\n * Languages\r\n * Slow chaning lists of application elements such as suppliers or parts\r\n * Positions in organizations\r\n * As an alternative to databases where the data set is small (1000 records) and changes infrequently, or on a controlled release schedule. \r\n * Defining menu items or actions\r\n\r\n## Related Proposals\r\n### Open Proposal https://github.com/fsharp/fslang-suggestions/issues/564\r\nIs a proposal to ease access to data elements common to all cases. Those elements are then accessible as properties using dot notation. \r\n\r\n```\r\ntype ShoppingCart = \r\n| EmptyCart of AppliedDiscountCode: string\r\n| CartWithItems of AppliedDiscountCode: string * Items: string list\r\n| CompletedCart of AppliedDiscountCode: string * Items: string list * CalculatedTotal: decimal\r\n\r\nv.AppliedDiscount //where v is of type ShoppingCart\r\n```\r\n\r\nThis comment https://github.com/fsharp/fslang-suggestions/issues/564#issuecomment-298170856 suggests that commen field be defined explicitly like this:\r\n\r\n```\r\ntype ShoppingCart =\r\n    val DiscountCode: string\r\n    | EmptyCart\r\n    | CartWithItems of Items: string list\r\n    | CompletedCart of Items: string list * CalculatedTotal: decimal\r\n```\r\n\r\nwith constructor  like this: `EmptyCart(DiscountCode=code)`. \r\n\r\nThe two proposals could perhaps be merged like this:\r\n\r\n```\r\ntype ShoppingCart =\r\n    val DiscountCode: string\r\n    | EmptyCart of DiscountCode = \"Free\"\r\n    | CartWithItems of DiscountCode = \"Moderate\"; Items: string list\r\n    | CompletedCart of DiscountCode = \"Full\"; Items: string list * CalculatedTotal: decimal\r\n```\r\nIn this case if the value is defined in the defintion that value is fixed for all \"instantiations\" of the case. One issue with this (other than being more complex) is that it blurs the line between \"constant values\" and dynamic values. \r\n\r\nIn this syntax the simple currency example is as follows: \r\n```\r\ntype Currency =\r\nval Name: string\r\nval Number: int \r\n| CAD Name = \"CAD\"; Number = 123\r\n| USD Name = \"USD\"; Number = 223\r\n| GBP Name = \"GBP\"; Number = 444\r\n\r\nprintfn \"The number of currency: '%s' is: %i\" CAD.Name CAD.Number\r\n```\r\n\r\n### Closed Proposal https://github.com/fsharp/fslang-suggestions/issues/558\r\nSuggests being able to define constant data for DU like so: \r\n\r\n```\r\ntype Planets =\r\n| MERCURY of {Mass=3.303e+23; Radius=2.4397e6)\r\n| VENUS of {Mass=4.869e+24; Radius=6.0518e6),\r\n| EARTH of {Mass=5.976e+24; Radius=6.37814e6),\r\n| MARS of {Mass=6.421e+23; Radius=3.3972e6),\r\n| JUPITER of {Mass=1.9e+27; Radius=7.1492e7),\r\n| SATURN of {Mass=5.688e+26; Radius=6.0268e7),\r\n| URANUS of {Mass=8.686e+25; Radius=2.5559e7),\r\n| NEPTUNE of {Mass=1.024e+26; Radius=2.4746e7);\r\n\r\n```\r\nHowever it does not suggest a way to make it easier to access the common set of data. \r\n\r\n\r\n## Existing approaches to this\r\n### Unions without Data\r\n```\r\ntype Currency = \r\n| CAD\r\n| USD\r\n| GBP\r\n```\r\n\r\nThis means the requirement that all currencies are enumerated but the data associated with Currencies is not modeled. The code can be obtained as a string using reflection but that is all.\r\n\r\n### Enums\r\n```\r\ntype Currency = \r\n| CAD=123\r\n| USD=223\r\n| GBP=444\r\n```\r\n\r\nIn this case the integer value is available as the data representing the Currency but again not all the needed data. \r\n\r\n### Link unions cases with records via let\r\n```\r\ntype Details = {\r\n    Code : Currency\r\n    Name : string\r\n    Number: int\r\n  } \r\ntype Currency =\r\n    Curency of Details\r\n\r\nlet CAD = Currency {Name = \"CAD\"; Number = 123}\r\nlet USD = Currency {Name = \"USD\"; Number = 223}\r\nlet GBP = Currency {Name = \"GBP\"; Number = 444}\r\n\r\nprintfn \"The number of currency: '%s' is: %i\" CAD.Name CAD.Number\r\n\r\n```\r\n\r\nThis supports all the data but is verbose. And to hide the Currency constructor to prevent the creation of invalid currencies will make it even more verbose.\r\n\r\n### Link unions cases with records via static members\r\n```\r\ntype Currency =\r\n| CAD \r\n| USD\r\n| GBP\r\nwith \r\nstatic private member AllDetails =\r\n  [\r\n  {Code = CAD; Name = \"CAD\"; Number = 123}\r\n  {Code = USD; Name = \"USD\"; Number = 223}]\r\n  {Code = GBP; Name = \"GBP\"; Number = 444}]\r\nmember this.Details = \r\n    Currency.AllDetails |> List.find (fun e -> e.Code = this)\r\nmember this.Name = \r\n   this.Details.Name\r\nmember this.Number = \r\n   this.Details.Number\r\n\r\nand Details = {\r\nCode : Currency\r\nName : string\r\nNumber: int\r\n} \r\n\r\nprintfn \"The number of currency: '%s' is: %i\" CAD.Name CAD.Number\r\n```\r\n\r\nAgain a bit verbose and tedious. It also suffers from the disadvantage of needing to keep the Union of currencies in line with the list of details. \r\n\r\nThis could also be done using a module to hold a function with `AllDetails`.\r\n\r\n## Pros and Cons \r\nThe advantages of making this adjustment to F# are that modeling complex static data in a typesafe way becomes very straight forward. See the example modeling Countries and Currencies above. Also see the examples of modeling the same domain with the current language definition.\r\n\r\nThe disadvantages of making this adjustment to F# are ...\r\n\r\n## Extra information\r\nEstimated cost (XS, S, M, L, XL, XXL): no clue\r\n\r\nRelated suggestions: https://github.com/fsharp/fslang-suggestions/issues/564,  https://github.com/fsharp/fslang-suggestions/issues/558\r\n\r\n\r\n## Affidavit (please submit!)\r\nPlease tick this by placing a cross in the box:\r\n* [x] This is not a question (e.g. like one you might ask on [stackoverflow](http://stackoverflow.com)) and I have searched stackoverflow for discussions of this issue\r\n* [x] I have [searched both open and closed suggestions on this site](http://github.com/fsharp/fslang-suggestions/issues) and believe this is not a duplicate\r\n* [x] This is not something which has obviously \"already been decided\" in previous versions of F#.  If you're questioning a fundamental design decision that has obviously already been taken (e.g. \"Make F# untyped\") then please don't submit it.\r\n\r\nPlease tick all that apply:\r\n* [x] This is not a breaking change to the F# language design (a least not as far as I can tell, which is not far)\r\n* [x] I or my company would be willing to help implement and/or test this (though my skills are limited)"}}
{"actor":{"display_login":"jbeeko"}}