{"id":13485667,"url":"https://github.com/xodio/hm-def","last_synced_at":"2025-07-20T07:34:32.350Z","repository":{"id":17712475,"uuid":"82304022","full_name":"xodio/hm-def","owner":"xodio","description":"Runtime type checking for JS with Hindley Milner signatures","archived":false,"fork":false,"pushed_at":"2022-12-03T15:42:33.000Z","size":777,"stargazers_count":199,"open_issues_count":13,"forks_count":11,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-06-20T18:18:36.565Z","etag":null,"topics":["functional-programming","hindley-milner","invariant","javascript","type-checking"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xodio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-17T14:18:11.000Z","updated_at":"2025-05-04T17:15:36.000Z","dependencies_parsed_at":"2023-01-13T19:27:57.007Z","dependency_job_id":null,"html_url":"https://github.com/xodio/hm-def","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/xodio/hm-def","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xodio%2Fhm-def","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xodio%2Fhm-def/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xodio%2Fhm-def/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xodio%2Fhm-def/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xodio","download_url":"https://codeload.github.com/xodio/hm-def/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xodio%2Fhm-def/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266086075,"owners_count":23874484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-programming","hindley-milner","invariant","javascript","type-checking"],"created_at":"2024-07-31T18:00:29.227Z","updated_at":"2025-07-20T07:34:32.326Z","avatar_url":"https://github.com/xodio.png","language":"JavaScript","funding_links":[],"categories":["类型检测","Type Checkers [🔝](#readme)","Type Checkers","Programming Tools","JavaScript"],"sub_categories":["Lenses"],"readme":"# Hindley Milner Definitions\n\nThe `hm-def` package allows you to enforce runtime type checking for JavaScript\nfunctions using Haskell-alike [Hindley\nMilner](https://github.com/ramda/ramda/wiki/Type-Signatures) type signatures.\n\nThe `hm-def` is build on top of\n[sanctuary-def](https://github.com/sanctuary-js/sanctuary-def)\nand basically just a syntax sugar for it.\n\n## Install\n\n```bash\n$ yarn add hm-def\n# or\n$ npm install hm-def\n```\n\n## Usage\n\nFirst, you need to create a function definition function.\n\n```javascript\nimport $ from 'sanctuary-def';\nimport {create} from 'hm-def';\n\nconst def = create ({\n  $,\n  checkTypes: true,\n  env: $.env,\n  typeClasses: [],\n});\n```\n\nThen instead of this:\n\n```javascript\nfunction sum(a, b) {\n  return a + b;\n}\n```\n\nyou can write:\n\n```javascript\nconst sum = def\n  ('sum :: Number -\u003e Number -\u003e Number')\n  (a =\u003e b =\u003e a + b);\n```\n\nAnd the calls to `sum` will be type checked:\n\n```javascript\nsum (42) (13);\n// 55\n\nsum ('42') (13);\n// TypeError: Invalid value\n//\n// foo :: Number -\u003e Number -\u003e Number\n//        ^^^^^^\n//          1\n//\n// 1)  \"42\" :: String\n//\n// The value at position 1 is not a member of ‘Number’.\n```\n\n### Arrays\n\nTo denote an array you enclose type of its elements in square brackets:\n\n```javascript\nconst magnitude = def\n  ('magnitude :: [Number] -\u003e Number')\n  (xs =\u003e Math.sqrt (xs.reduce ((acc, x) =\u003e acc + x * x, 0)));\n\nmagnitude ([3, 4, 0]);\n// 5\n\nmagnitude (3, 4, 0);\n// TypeError: Function applied to too many arguments\n//\n// magnitude :: Array Number -\u003e Number\n//\n// ‘magnitude’ expected at most one argument but received three arguments.\n```\n\nActually it’s just a shortcut to a more general:\n\n```javascript\nconst magnitude = def\n  ('magnitude :: Array Number -\u003e Number')\n  (xs =\u003e Math.sqrt (xs.reduce ((acc, x) =\u003e acc + x * x, 0)));\n```\n\nWhere `Array` is a regular unary type provided by the default environment.\nIt takes a single type argument which describes the type of array’s elements.\n\n### Records\n\nTo denote objects with a known schema record syntax is used:\n\n```javascript\nconst minMax = def\n  ('minMax :: [Number] -\u003e { min :: Number, max :: Number }')\n  (xs =\u003e xs.reduce (\n    (acc, x) =\u003e ({\n      min: Math.min (x, acc.min),\n      max: Math.max (x, acc.max),\n    }),\n    { min: Infinity, max: -Infinity }\n  ));\n\nminMax ([1, 4, 6, 3, 4, 5, -3, 4]);\n// { min: -3, max: 6 }\n```\n\n### Maps\n\nTo describe a map of homogenous data you can use `StrMap` type:\n\n```javascript\nconst occurrences = def\n  ('occurrences :: [String] -\u003e StrMap Number')\n  (xs =\u003e xs.reduce (\n    (acc, x) =\u003e {\n      // a bit of dirty local mutation\n      acc[x] = (acc[x] || 0) + 1;\n      return acc;\n    },\n    {}\n  ));\n\noccurrences (['foo', 'bar', 'bar', 'baz', 'bar', 'qux', 'foo']);\n// {\n//   foo: 2,\n//   bar: 3,\n//   baz: 1,\n//   qux: 1,\n// }\n```\n\n### Types available\n\nYou pass type definitions with `env` option of `HMD.create`. `$.env` from\n`sanctuary-def` provides type info for all built-in types:\n\n- AnyFunction\n- Arguments\n- Array\n- Boolean\n- Date\n- Error\n- HtmlElement\n- Null\n- Number\n- Object\n- RegExp\n- StrMap\n- String\n- Symbol\n- Undefined\n\nYou would likely to add your own application domain types. See [documentation\nof type\nconstructors](https://github.com/sanctuary-js/sanctuary-def#type-constructors)\nto learn how.\n\n### Type constraints\n\nFor most generic functions you’d like to add type constraints. Consider the\nfunction:\n\n```javascript\nconst concat = def\n  ('concat :: a -\u003e a -\u003e a')\n  (y =\u003e x =\u003e x.concat (y));\n\nconcat ([3, 4]) ([1, 2]);\n// [1, 2, 3, 4]\n\nconcat (' world') ('Hello')\n// 'Hello world'\n\nconcat (42) (13)\n// TypeError: x.concat is not a function\n```\n\nThe call to the function crashed on invalid argument types post factum. We can\nplace a type constraint on `a` to fail in advance with a more clear message.\n\nType constraints are done with type classes. There are many type classes\nprovided by\n[sanctuary-type-classes](https://github.com/sanctuary-js/sanctuary-type-classes)\nand you can create your own.\n\nTo use HM definitions with type class constaints you should provide `typeClasses`\noption with classes you’d like to use later:\n\n```javascript\nimport $ from 'sanctuary-def';\nimport Z from 'sanctuary-type-classes';\nimport {create} from 'hm-def';\n\nconst def = create ({\n  $,\n  checkTypes: true,\n  env: $.env,\n  typeClasses: [\n    // ...\n    Z.Functor,\n    Z.Semigroup,\n    // ...\n  ],\n});\n```\n\nThen:\n\n```javascript\nconst concat = def\n  ('concat :: Semigroup a =\u003e a -\u003e a -\u003e a')\n  (y =\u003e x =\u003e x.concat (y));\n\nconcat ([3, 4]) ([1, 2]);\n// [1, 2, 3, 4]\n\nconcat (' world') ('Hello')\n// 'Hello world'\n\nconcat (42) (13)\n// TypeError: Type-class constraint violation\n//\n// foo :: Semigroup a =\u003e a -\u003e a -\u003e a\n//        ^^^^^^^^^^^    ^\n//                       1\n//\n// 1)  42 :: Number\n//\n// ‘foo’ requires ‘a’ to satisfy the Semigroup type-class constraint; the value\n// at position 1 does not.\n```\n\n\u003ca name=\"type-constructors\"\u003e\u003c/a\u003e\n\n### Type constructors\n\n_Added in v0.3.0_\n\nIf you need UnaryType or BinaryType of something you should add them into `env`\nwith `$.Unknown` types in it. Then `hm-def` will recreate specific types when you\nwill define your functions.\n\nAssuming we have an implementation of `Either a b` exposed as `Either`.\n\n```javascript\nconst EitherType = $.BinaryType\n  ('my-package/Either')\n  ('http://example.com/my-package#Either')\n  (x =\u003e x != null \u0026\u0026 x['@@type'] === 'my-package/Either')\n  (either =\u003e (either.isLeft ? [either.value] : []))\n  (either =\u003e (either.isRight ? [either.value] : []));\n// EitherType is a function `EitherType :: Type -\u003e Type -\u003e Type`,\n\nconst def = HMD.create ({\n  $,\n  checkTypes: true,\n  env: $.env.concat ([\n    EitherType ($.Unknown) ($.Unknown),\n  ]),\n});\n\n// Now we can just define functions as usual:\nconst foo = def\n  ('foo :: Either Number String -\u003e Either String String')\n  ((x) =\u003e x.chain ((val) =\u003e {\n    if (val \u003e= 3) return Either.Right ('It greater than or equal 3');\n    return Either.Left ('It less than 3');\n  }));\n\nfoo (Either.Right (4)); // Either.Right('It greater than or equal 3')\nfoo (Either.Right (1)); // Either.Left('It less than 3')\nfoo (Either.Right ('hello')); // TypeError: The value at position 1 is not a member of ‘Number’\nfoo (1); // TypeError: The value at position 1 is not a member of ‘Either Number String’\n```\n\n### Currying\n\nBeginning with `1.0.0`, functions are not automatically curried, and they are\nexpected to be manually curried at all times:\n\n```javascript\nimport $ from 'sanctuary-def';\nimport {create} from 'hm-def';\n\nconst def = create ({\n  $,\n  checkTypes: true,\n  env: $.env,\n  typeClasses: [],\n});\n\nconst foo = def\n  ('foo :: a -\u003e b -\u003e c')\n  (x =\u003e y =\u003e x + y);\n\nfoo (1) (2);\n// 3\n\nfoo (1, 2);\n// TypeError: ‘foo’ applied to the wrong number of arguments\n//\n// foo :: a -\u003e b -\u003e c\n//        ^\n//        1\n//\n// Expected one argument but received two arguments:\n//\n//   - 1\n//   - 2\n\nconst bar = def\n  ('bar :: a -\u003e b -\u003e c')\n  ((x, y) =\u003e x + y);\n\nbar (1, 2);\n// TypeError: ‘bar’ applied to the wrong number of arguments\n//\n// bar :: a -\u003e b -\u003e c\n//        ^\n//        1\n//\n// Expected one argument but received two arguments:\n//\n//   - 1\n//   - 2\n```\n\nThis is consistent with `sanctuary`'s way of currying, known as\n[\"familiar currying\"](https://github.com/sanctuary-js/sanctuary/issues/438).\n\n## Changelog\n\n### 1.0.0\n\n* Update `sanctuary-*`, building, and testing dependencies.\n* Breaking :exclamation: functions are no longer curried automatically. See the\n  [currying section](#currying).\n\n### 0.3.0\n\n* Update `sanctuary-def` dependency to version 0.14.0\n* BREAKING :exclamation: All Unary/Binary Types with variable types inside should be\n  specified in `env` with `$.Unknown` types. Then, when you define functions, `hm-def`\n  will recreate specific types for these functions. (See more)[#type-constructors]\n\n  Since version 0.10.0 of `sanctuary-def` environments must be of type `Array Type`.\n  So it must not contain type constructors anymore.\n  ([sanctuary-js/sanctuary-def#124](https://github.com/sanctuary-js/sanctuary-def/pull/124))\n\n### 0.2.1\n\n* Update `ramda` dependency to version 0.24.1\n\n### 0.2.0\n\n* Add `def.curried`\n* Fix errors when using some non-nullary types like built-in `Array` or `StrMap`\n\n## Contributors\n\nAlphabetically:\n\n* [davidchambers](https://github.com/davidchambers)\n* [evgenykochetkov](https://github.com/evgenykochetkov)\n* [Gipphe](https://github.com/Gipphe)\n* [nkrkv](https://github.com/nkrkv)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxodio%2Fhm-def","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxodio%2Fhm-def","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxodio%2Fhm-def/lists"}