{"id":24977790,"url":"https://github.com/adzz/a_range","last_synced_at":"2025-06-10T09:03:28.488Z","repository":{"id":68119354,"uuid":"350351283","full_name":"Adzz/a_range","owner":"Adzz","description":"Ranges in Elixir for more than just numbers","archived":false,"fork":false,"pushed_at":"2023-02-07T14:15:48.000Z","size":17,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-10T09:02:31.040Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Adzz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-22T13:20:23.000Z","updated_at":"2021-07-25T00:19:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"a35b3369-ba15-4e34-befd-b36501e271c8","html_url":"https://github.com/Adzz/a_range","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Adzz%2Fa_range","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Adzz%2Fa_range/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Adzz%2Fa_range/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Adzz%2Fa_range/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Adzz","download_url":"https://codeload.github.com/Adzz/a_range/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Adzz%2Fa_range/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259043759,"owners_count":22797159,"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":[],"created_at":"2025-02-03T23:09:16.752Z","updated_at":"2025-06-10T09:03:28.463Z","avatar_url":"https://github.com/Adzz.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ARange\n\nAlpha version - implementation may change.\n\nRanges in elixir have some limitations. Right now they are only for integer values and you can't make ranges for letters for example. This library lets you define any arbitrary range and have Enum functions work with it automatically.\n\n### How it works\n\nRanges can be thought of as streams of values that start at some value, progress to a next value and halt at a final value. We need to be able to step forwards and backwards through a range and determine if a given value exists within a range.\n\nConceptually all ranges have a notion of some kind of order - element being before or after other elements. We therefore require user defined ranges to implement specific functions that allow for the Enumerable protocol to work. These functions can be found in the ARange behaviour.\n\nTo create a range you must create a module and implement the ARange behaviour's callbacks:\n\n```elixir\n@doc \"\"\"\nGets given the current value and should return whatever should be considered the next value.\n\"\"\"\n@callback next(any()) :: any()\n\n@doc \"\"\"\nGets given the current value and should return whatever should be considered the previous value.\n\"\"\"\n@callback previous(any()) :: any()\n\n@doc \"\"\"\nAccepts any given value and should return whether or not the value exists in the range\n\"\"\"\n@callback included?(any(), any(), any()) :: any()\n\n@doc \"\"\"\nAccepts the first and last value in the range and returns how many elements are in the given range\n\"\"\"\n@callback count(any(), any()) :: any()\n```\n\nsee the examples below\n\n\n### Examples\n\nThe following shows how you can create a Range of English language letters which increment by 1 codepoint.\n\n```elixir\ndefmodule Letterz do\n  @behaviour ARange\n  @moduledoc \"\"\"\n  Enables creation of ranges of letters which increment by 1 codepoint.\n  \"\"\"\n\n  @impl true\n  def next(letter) do\n    [code_point] = String.to_charlist(letter)\n    \u003c\u003ccode_point + 1\u003e\u003e\n  end\n\n  @impl true\n  def previous(letter) do\n    [code_point] = String.to_charlist(letter)\n    \u003c\u003ccode_point - 1\u003e\u003e\n  end\n\n  @impl true\n  def included?(start, end_value, letter) do\n    [start_code_point] = String.to_charlist(start)\n    [end_code_point] = String.to_charlist(end_value)\n\n    with [code_point] \u003c- String.to_charlist(letter) do\n      code_point \u003c= end_code_point \u0026\u0026 code_point \u003e= start_code_point \u0026\u0026\n        rem(code_point - start_code_point, 1) == 0\n    else\n      _ -\u003e false\n    end\n  end\n\n  @impl true\n  def count(start, end_value) do\n    [start_code_point] = String.to_charlist(start)\n    [end_code_point] = String.to_charlist(end_value)\n\n    if start_code_point \u003c= end_code_point do\n      end_code_point - start_code_point + 1\n    else\n      start_code_point - end_code_point + 1\n    end\n  end\nend\n\nletters = ARange.new(\"a\", \"b\", Letter)\n# Or\nletters = ARange.new(%{start: \"a\", end: \"z\", type: Letter})\nnext = ARange.next(letters) |\u003e ARange.next() |\u003e ARange.next() |\u003e ARange.current_value()\nEnum.reduce(letters, [], fn l, acc -\u003e [l | acc] end)\n```\n\n### Pattern Matching.\n\nPattern matching on a range is not really possible because you can't execute a function in a guard. Instead you can put it as the first line of your function:\n\n```elixir\ndef my_fun(value) do\n  if ARange.includes?(my_range, value) do\n    a_thing()\n  else\n    another_thing()\n  end\nend\n```\n\n\u003c!--\nCan we do some kind of pattern thing with this library...\nA standardised pattern to match on for inclusion.\nSuppose we should read the paper though.\n--\u003e\n\n## Installation\n\nIf [available in Hex](https://hex.pm/docs/publish), the package can be installed\nby adding `a_range` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:a_range, \"~\u003e 0.1.0\"}\n  ]\nend\n```\n\nDocumentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)\nand published on [HexDocs](https://hexdocs.pm). Once published, the docs can\nbe found at [https://hexdocs.pm/a_range](https://hexdocs.pm/a_range).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadzz%2Fa_range","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadzz%2Fa_range","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadzz%2Fa_range/lists"}