{"action":"created","issue":{"url":"https://api.github.com/repos/ramda/ramda-fantasy/issues/67","labels_url":"https://api.github.com/repos/ramda/ramda-fantasy/issues/67/labels{/name}","comments_url":"https://api.github.com/repos/ramda/ramda-fantasy/issues/67/comments","events_url":"https://api.github.com/repos/ramda/ramda-fantasy/issues/67/events","html_url":"https://github.com/ramda/ramda-fantasy/pull/67","id":101148966,"number":67,"title":"Adds sum and product types.","user":{"login":"scott-christopher","id":8951601,"avatar_url":"https://avatars.githubusercontent.com/u/8951601?v=3","gravatar_id":"","url":"https://api.github.com/users/scott-christopher","html_url":"https://github.com/scott-christopher","followers_url":"https://api.github.com/users/scott-christopher/followers","following_url":"https://api.github.com/users/scott-christopher/following{/other_user}","gists_url":"https://api.github.com/users/scott-christopher/gists{/gist_id}","starred_url":"https://api.github.com/users/scott-christopher/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scott-christopher/subscriptions","organizations_url":"https://api.github.com/users/scott-christopher/orgs","repos_url":"https://api.github.com/users/scott-christopher/repos","events_url":"https://api.github.com/users/scott-christopher/events{/privacy}","received_events_url":"https://api.github.com/users/scott-christopher/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2015-08-15T07:04:44Z","updated_at":"2015-08-15T10:20:39Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/ramda/ramda-fantasy/pulls/67","html_url":"https://github.com/ramda/ramda-fantasy/pull/67","diff_url":"https://github.com/ramda/ramda-fantasy/pull/67.diff","patch_url":"https://github.com/ramda/ramda-fantasy/pull/67.patch"},"body":"This changes adds support for creating sum and product ADTs. I'm not entirely sure this belongs in ramda-fantasy or not, but it does make declaring data types clean and consistent (at least in my opinion).\r\n\r\nI've also got a separate branch with the existing types converted to using ADTs for those who are curious: https://github.com/scott-christopher/ramda-fantasy/blob/adt/src/Maybe.js (I wanted to keep this PR focussed on the ADT implementation itself, before discussing changing anything else).\r\n\r\nI've added the comments from the code below that describes the general usage.\r\n\r\n---\r\n\r\n## Product types\r\n\r\nProduct types can be created by calling `product` with strings representing the fields to exist on each type.\r\n\r\nFor example, the following will create a `Point` type with two fields (`x` and `y`):\r\n```js\r\nvar Point = Type.product('x', 'y');\r\n```\r\n\r\nInstances are created by calling the type as a function:\r\n```js\r\nvar origin = Point(0, 0);\r\norigin instanceof Point // true\r\n```\r\n\r\nOnce an instance is created, its fields can be accessed via getter methods attached to the instance:\r\n```js\r\nvar originX = origin.x();\r\n```\r\n\r\nAlternatively an instance can be unapplied, providing access to all its fields in one call:\r\n```js\r\norigin.unapply(function(x, y) {\r\n  return x === 0 && y === 0;\r\n});\r\n```\r\n\r\nValues of a product instance can also be modified, however because the instances are immutable this will cause a new instance to be returned with the modified value rather than mutating the previous instance.\r\n```js\r\nvar x1y0 = origin.x(1);\r\nx1y0.x(); // 1\r\n\r\n// note `origin` remains unmodified\r\norigin.x(); // 0\r\n```\r\n\r\nWhen a type is created, lenses for each field are also available on the type constructor:\r\n```js\r\nR.view(Point.x, point); // 0\r\nR.set(Point.y, point);  // Point(0, 1);\r\n```\r\n\r\n## Sum Types\r\n\r\nSum types can be created by calling `sum` with an object that describes its constructors.\r\n\r\nFor example will create a `List` type, with two constructors `Cons` and `Nil`:\r\n```js\r\nvar List = Type.sum({ Cons: ['head', 'tail'], Nil: [] });\r\nvar Cons = List.Cons;\r\nvar Nil = List.Nil;\r\n```\r\n\r\nThe constructors behave exactly the same as product type constructors:\r\n```js\r\nvar abc = Cons('a', Cons('b', Cons('c', Nil)));\r\nabc instanceof Cons; // true\r\nabc instanceof List; // true\r\n\r\nvar c = abc.tail().tail().head();\r\n// or\r\nvar c = R.view(R.compose(Cons.tail, Cons.tail, Cons.head), abc);\r\n\r\nabc.unapply(function(head, tail) {\r\n  return head + head + head;\r\n}); // \"aaa\"\r\n```\r\n\r\nIf a constructor is declared with no fields, a singleton instance of that constructor is returned instead:\r\n```js\r\nNil instanceof List; // true\r\nNil.unapply(function() { return '?'; }); // '?'\r\n```\r\n\r\nAn instance can also be matched and dispatched against its type:\r\n```js\r\nfunction headOrElse(other, list) {\r\n  return list.match({\r\n    Cons: function(head, tail) { return head; },\r\n    Nil: function() { return other; }\r\n  });\r\n}\r\n```"},"comment":{"url":"https://api.github.com/repos/ramda/ramda-fantasy/issues/comments/131327071","html_url":"https://github.com/ramda/ramda-fantasy/pull/67#issuecomment-131327071","issue_url":"https://api.github.com/repos/ramda/ramda-fantasy/issues/67","id":131327071,"user":{"login":"scott-christopher","id":8951601,"avatar_url":"https://avatars.githubusercontent.com/u/8951601?v=3","gravatar_id":"","url":"https://api.github.com/users/scott-christopher","html_url":"https://github.com/scott-christopher","followers_url":"https://api.github.com/users/scott-christopher/followers","following_url":"https://api.github.com/users/scott-christopher/following{/other_user}","gists_url":"https://api.github.com/users/scott-christopher/gists{/gist_id}","starred_url":"https://api.github.com/users/scott-christopher/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scott-christopher/subscriptions","organizations_url":"https://api.github.com/users/scott-christopher/orgs","repos_url":"https://api.github.com/users/scott-christopher/repos","events_url":"https://api.github.com/users/scott-christopher/events{/privacy}","received_events_url":"https://api.github.com/users/scott-christopher/received_events","type":"User","site_admin":false},"created_at":"2015-08-15T10:20:39Z","updated_at":"2015-08-15T10:20:39Z","body":"It doesn't implement any of the FL spec, as it is intended to potentially be used as a base for implementing the other types in ramda-fantasy.\r\n\r\nThe main benefits are:\r\n- the instances are immutable\r\n- it provides a consistent approach to defining types in ramda-fantasy\r\n```js\r\nvar Maybe = Type.sum({ Just: ['value'], Nothing: []);\r\n```\r\n- it offers pattern matching against types for extracting values (one of the stronger benefits I believe):\r\n```js\r\n// rather than accessing values via conditional guards or inspecting values like so\r\nif (Maybe.isJust(maybeThing)) {\r\n  x = maybeThing.value;\r\n} else {\r\n  x = someOtherValue;\r\n}\r\n// can instead be accessed via matching against types\r\nx = maybeThing.match({ Just: R.identity, Nothing: R.always(someOtherValue) });\r\n```\r\n- data constructors are curried\r\n```js\r\nvar Tuple = Type.product('_1', '_2');\r\nR.map(Tuple('a'), [0, 1, 2]); // [Tuple('a', 0), Tuple('a', 1), Tuple('a', 2)]\r\n```\r\n- instances can be unapplied to extract all values via a function\r\n```js\r\nvar Point = Type.product('x', 'y');\r\npoint(2, 4).unapply((x, y) => y / x); // 2\r\n```\r\n\r\nIf there are still concerns about whether it should be part of ramda-fantasy (or even just demoted to the `internal` folder and not exposed via the public `index.js`), I'm happy to move it to a different project.\r\n\r\nAlternatively, if there are concerns regarding the naming, we could potentially rename the functions to something less formal. Perhaps even just offer a singe `Type` function that accepts either an array for creating product types and an object for sum types. :shrug:\r\n\r\n---\r\nAnd the linting errors were the ones I resolved in the previous PR ;) – I have since rebased."}}