{"id":19917189,"url":"https://github.com/athos/sweet-array","last_synced_at":"2025-04-06T07:15:53.596Z","repository":{"id":62432363,"uuid":"324138614","full_name":"athos/sweet-array","owner":"athos","description":"Array manipulation library for Clojure with \"sweet\" array type notation and more safety by static types","archived":false,"fork":false,"pushed_at":"2025-02-15T13:39:18.000Z","size":97,"stargazers_count":65,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-30T06:10:01.063Z","etag":null,"topics":["arrays","clojure","type-hints"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/athos.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","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":"2020-12-24T11:17:58.000Z","updated_at":"2025-02-15T13:26:06.000Z","dependencies_parsed_at":"2024-08-14T08:29:55.766Z","dependency_job_id":"f95b4907-2143-4195-aece-6e25178bed7d","html_url":"https://github.com/athos/sweet-array","commit_stats":{"total_commits":117,"total_committers":1,"mean_commits":117.0,"dds":0.0,"last_synced_commit":"251fd7e03ebda9563696ba28e32effa324fdab3f"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athos%2Fsweet-array","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athos%2Fsweet-array/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athos%2Fsweet-array/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athos%2Fsweet-array/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/athos","download_url":"https://codeload.github.com/athos/sweet-array/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247445682,"owners_count":20939961,"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":["arrays","clojure","type-hints"],"created_at":"2024-11-12T21:49:06.017Z","updated_at":"2025-04-06T07:15:53.569Z","avatar_url":"https://github.com/athos.png","language":"Clojure","readme":"# sweet-array\n[![Clojars Project](https://img.shields.io/clojars/v/dev.athos/sweet-array.svg)](https://clojars.org/dev.athos/sweet-array)\n![build](https://github.com/athos/sweet-array/workflows/build/badge.svg)\n[![codecov](https://codecov.io/gh/athos/sweet-array/branch/main/graph/badge.svg?token=phoLI2vS9n)](https://codecov.io/gh/athos/sweet-array)\n\nArray manipulation library for Clojure with \"sweet\" array type notation and more safety by static types\n\n## Table of Contents\n\n- [Rationale](#rationale)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Array creation](#array-creation)\n  - [Array definition](#array-definition)\n  - [Array indexing](#array-indexing)\n  - [Type-related utilities](#type-related-utilities)\n- [Array type notation](#array-type-notation)\n\n## Rationale\n\nClojure has built-in support for Java arrays and provides a set of\nfacilities for manipulating them, including `make-array`, `aget`, `aset` and so on.\nHowever, some of their difficulties like the following tend to lead users to\nwrite verbose or unexpectedly inefficient code:\n\n- Need to use different constructor functions for different types and dimensionalities\n- Clojure compiler sometimes does not know the static type of arrays and may emit inefficient bytecode (especially for multi-dimensional arrays)\n- Array type hints tend to be cryptic (e.g. `[[D`, `[Ljava.lang.String;`, etc.) and can occasionally be quite hard for humans to write manually\n  - Array class syntax introduced in Clojure 1.12 have mitigated this issue to some extent\n\nThese issues have been pointed out by various Clojurians out there in the past:\n\n- [Taming multidim Arrays](http://clj-me.cgrand.net/2009/10/15/multidim-arrays/)  by Christophe Grand (@cgrand)\n- [Java arrays and unchecked math](http://clojure-goes-fast.com/blog/java-arrays-and-unchecked-math/) by Clojure Goes Fast\n- [CLJ-1289: aset-* and aget perform poorly on multi-dimensional arrays even with type hints](https://clojure.atlassian.net/browse/CLJ-1288)\n\n`sweet-array` aims to provide solutions for them. Contretely:\n\n- It defines *array type descriptors*, a concise and intuitive array type notation, and provides a generic array constructor which can be used for any types and dimensionalities\n- The array constructors in the library maintain the static type of arrays, which reduces the cases where users have to add type hints manually\n- The array operators in the library automatically infer the resulting array type, so even multi-dimensional arrays can be handled efficiently\n\nAs a result, we can write code like the following using `sweet-array`:\n\n```clojure\n;; Example of multiplying two arrays as matrices\n\n(require '[sweet-array.core :as sa])\n\n(sa/def a (sa/new [[double]] [[1.0 2.0] [3.0 4.0]]))\n(sa/def b (sa/new [[double]] [[5.0 6.0] [7.0 8.0]]))\n\n(let [nrows (alength a)\n      ncols (alength (sa/aget b 0))\n      n (alength b)\n      c (sa/new [[double]] nrows ncols)]\n  (dotimes [i nrows]\n    (dotimes [j ncols]\n      (dotimes [k n]\n        (sa/aset c i j\n                 (+ (* (sa/aget a i k)\n                       (sa/aget b k j))\n                    (sa/aget c i j))))))\n  c)\n```\n\nInstead of:\n\n```clojure\n(def ^double/2 a (into-array [(double-array [1.0 2.0]) (double-array [3.0 4.0])]))\n(def ^double/2 b (into-array [(double-array [5.0 6.0]) (double-array [7.0 8.0])]))\n\n(let [nrows (alength a)\n      ncols (alength ^double/1 (aget b 0))\n      n (alength b)\n      ^double/2 c (make-array Double/TYPE nrows ncols)]\n  (dotimes [i nrows]\n    (dotimes [j ncols]\n      (dotimes [k n]\n        (aset ^double/1 (aget c i) j\n              (+ (* (aget ^double/1 (aget a i) k)\n                    (aget ^double/1 (aget b k) j))\n                 (aget ^double/1 (aget c i) j))))))\n  c)\n```\n\nNote that all the type hints in this code are mandatory to make it run as fast as the above one with `sweet-array`.\n\n## Installation\n\nAdd the following to your `deps.edn` / `project.clj`:\n\n- `deps.edn`\n```\n{dev.athos/sweet-array {:mvn/version \"0.2.0\"}}\n```\n\n- `project.clj`\n```\n[dev.athos/sweet-array \"0.2.0\"]\n```\n\n## Usage\n\n### Array creation\n\n#### `(new [T] n1 n2 ... nk)`\n\nThe simplest way to create an array using this library is to use\nthe `sweet-array.core/new` macro. The `new` macro is a generic array constructor\nwhich can be used to create both primitive and reference type arrays:\n\n```clojure\n(require '[sweet-array.core :as sa])\n\n(def xs (sa/new [int] 3))\n(class xs) ;=\u003e int/1, which means int array type\n(alength xs) ;=\u003e 3\n\n(def ys (sa/new [String] 5))\n(class ys) ;=\u003e [Ljava.lang.String;, which means String array type\n(alength ys) ;=\u003e 5\n```\n\nThe first argument of the `new` macro is what we call an *array type descriptor*.\nSee the [Array type notation](#array-type-notation) section for more details, but roughly speaking,\nan array type descriptor `[T]` denotes an array type whose component type is `T`\n(e.g. `[int]` denotes the int array type and `[String]` denotes the String array type).\n\nThe `new` macro can also be used to create multi-dimensional arrays.\nThe following example creates a two-dimensional int array:\n\n```clojure\n(def arr (sa/new [[int]] 2 3))\n(class arr) ;=\u003e int/2, which means 2-d int array type\n(alength arr) ;=\u003e 2\n(alength (aget arr 0)) ;=\u003e 3\n```\n\nIn general, `(sa/new [[T]] n1 n2)` produces a 2-d array of type `T` of size `n1`x`n2`\nand `(sa/new [[[T]]] n1 n2 n3)` produces a 3-d array of type `T` of size `n1`x`n2`x`n3`,\nand so on.\n\n\u003e [!NOTE]\n\u003e For primitive types, `T` can also be represented as a keyword instead of a bare symbol\n\u003e (as in `[:int]` or `[[:double]]`). This is useful to avoid issues with automatic\n\u003e namespace qualification in the syntax quote and false alerts reported by the linter.\n\nSince 0.3.0, [array class syntax](https://github.com/clojure/clojure/blob/master/changes.md#27-array-class-syntax) introduced in Clojure 1.12 can be used interchangeably with array type descriptors anywhere within the library:\n\n```clojure\n(def arr1 (sa/new String/1 5))\n(class arr1) ;=\u003e java.lang.String/1\n\n(def arr2 (sa/new int/2 2 3))\n(class arr2) ;=\u003e int/2\n```\n\n#### `(new [T] [e1 e2 ... ek])`\n\nThe `new` macro provides another syntax to create an array enumerating\nthe initial elements. `(sa/new [T] [e1 e2 ... ek])` creates an array\ninitialized with the elements `e1`, `e2`, ..., `ek`:\n\n```clojure\n(def arr (sa/new [int] [1 2 3]))\n(alength arr) ;=\u003e 3\n[(aget arr 0) (aget arr 1) (aget arr 2)] ;=\u003e [1 2 3]\n```\n\nIn general, `(sa/new [T] [e1 e2 ... ek])` is equivalent to:\n\n```clojure\n(doto (sa/new [T] k)\n  (aset 0 e1)\n  (aset 1 e2)\n  ...\n  (aset (- k 1) ek))\n```\n\nThis form can be used to initialize arrays of any dimensionality:\n\n```clojure\n;; 2-d double array\n(sa/new [[double]] [[1.0 2.0] [3.0 4.0]])\n;; 3-d boolean array\n(sa/new [[[boolean]]]\n        [[[true false] [false true]]\n         [[false true] [true false]]]\n```\n\nWhen initializing multi-dimensional arrays, the init expression for each element\nmay itself be an array or an expression that evaluates to an array:\n\n```clojure\n(def xs (sa/new [double] [1.0 2.0]))\n(def ys (sa/new [double] [3.0 4.0]))\n(sa/new [[double]] [xs ys])\n```\n\n#### `(into-array [T] coll)`\n\nAnother way to create an array is to use the `sweet-array.core/into-array` macro:\n\n```clojure\n(require '[sweet-array.core :as sa])\n\n(def arr (sa/into-array [int] (range 10)))\n(class arr) ;=\u003e int/1\n(alength arr) ;=\u003e 10\n```\n\nLike `clojure.core/into-array`, `sa/into-array` converts an existing collection\n(Seqable) into an array. Unlike `clojure.core/into-array`, the resulting array\ntype is specified with the [array type descriptor](#array-type-notation) as the first argument.\n\n`sa/into-array` can also be used to create multi-dimensional arrays:\n\n```clojure\n(def arr' (sa/into-array [[int]] (partition 2 (range 10))))\n(class arr') ;=\u003e int/2\n[(aget arr' 0 0) (aget arr' 0 1) (aget arr' 1 0) (aget arr' 1 1)]\n;=\u003e [0 1 2 3]\n```\n\n#### `(into-array [T] xform coll)`\n\nThe `sa/into-array` macro optionally takes a [transducer](https://clojure.org/reference/transducers).\nThis form is inspired by and therefore analogous to `(into to xform from)`.\nThat is, the transducer `xform` as the second argument will be applied\nwhile converting the collection into an array:\n\n```clojure\n(def arr (sa/into-array [int] (filter even?) (range 10)))\n(alength arr) ;=\u003e 5\n[(aget arr 0) (aget arr 1) (aget arr 2)] ;=\u003e [0 2 4]\n```\n\nThis is especially useful to do transformations that increase or decrease\nthe dimensionality of an array:\n\n```clojure\n;; 1-d to 2-d conversion\n(sa/into-array [[int]] (partition-all 2) (sa/new [int] [1 2 3 4]))\n\n;; 2-d to 1-d conversion\n(sa/into-array [double] cat (sa/new [[double]] [[1.0 2.0] [3.0 4.0]]))\n```\n\n### Array definition\n\n#### `(def name init)`\n#### `(def name docstring init)`\n\nSince 0.2.0, `sweet-array` provides its own version of the `def` macro.\nIt can be used as a drop-in replacement of Clojure's `def`. Unlike the ordinary\n`def` form, it infers the static type of `init` and implicitly adds the inferred\nUsing the `def` macro together with other macros from this library, it's hardly\nnecessary to add a type hint explicitly:\n\n```clojure\n(sa/def arr (sa/new [int] [1 2 3]))\n\n(:tag (meta #arr))\n;=\u003e [I\n```\n\nNote that the `def` macro will throw an error at expansion time if the type of\nthe `init` expression cannot be inferred or the inferred type is not an array\ntype:\n\n```clojure\n(sa/def arr (identity (sa/new [int] [1 2 3])))\n;; Syntax error macroexpanding sweet-array.core/def* at (REPL:1:1).\n;; Can't infer the static type of (identity (sa/new [int] [1 2 3])). Use `sweet-array.core/cast` to explicitly specify the array type or use `def` instead.\n\n(sa/def arr 42)\n;; Syntax error macroexpanding sweet-array.core/def* at (REPL:1:1).\n;; Can't use sweet-array.core/def for 42, which is long, not array\n```\n\n### Array indexing\n\n#### `(aget array idx1 idx2 ... idxk)`\n#### `(aset array idx1 idx2 ... idxk val)`\n\n`sweet-array` provides its own version of `aget` / `aset` for indexing arrays.\nThey work almost the same way as `aget` / `aset` defined in `clojure.core`:\n\n```clojure\n(require '[sweet-array.core :as sa])\n\n(sa/def arr (sa/new [int] [1 2 3 4 5]))\n\n(sa/aget arr 2) ;=\u003e 3\n(sa/aset arr 2 42)\n(sa/aget arr 2) ;=\u003e 42\n```\n\nOf course, they can also be used for multi-dimensional arrays as\n`c.c/aget` \u0026 `aset`:\n\n```clojure\n(sa/def arr (sa/new [double] [[1.0 2.0] [3.0 4.0]]))\n\n(sa/aget arr 1 1) ;=\u003e 4.0\n(sa/aset arr 1 1 42)\n(sa/aget arr 1 1) ;=\u003e 42\n```\n\nThe difference is that `sa/aget` and `sa/aset` infer the static type of their\nfirst argument and utilize it for several purposes as follows.\nIn a nutshell, they are safer and faster:\n\n- Static type checking for the array argument\n  - If the type inference fails, they will fall back to `c.c/aget` \u0026 `aset` and emit an reflection warning\n    ```clojure\n    (set! *warn-on-reflection* true)\n\n    (fn [arr] (sa/aget arr 0))\n    ;; Reflection warning, ... - call to static method aget on clojure.lang.RT can't be resolved (argument types: unknown, int).\n\n    (fn [arr] (sa/aget arr 0 0))\n    ;; Reflection warning, ... - type of first argument for aget cannot be inferred\n    ```\n  - If the type inference succeeds but the inferred type of the first argument is not an array type, then they will raise a compile-time error\n    ```clojure\n    (sa/aget \"I'm a string\" 0)\n    ;; Syntax error macroexpanding sweet-array.core/aget* at ...\n    ;; Can't apply aget to \"I'm a string\", which is java.lang.String, not array\n    ```\n  - If more indices are passed to them than the inferred array type expects, then they will raise a compile-time error\n    ```clojure\n    (sa/aget (sa/new [int] 3) 0 1 2)\n    ;; Syntax error macroexpanding sweet-array.core/aget* at ...\n    ;; Can't apply aget to (sa/new [int] 3) with more than 1 index(es)\n    ```\n- Faster access to multi-dimensional arrays by automatic type hint insertion\n  - `sa/aget` \u0026 `sa/aset` know that indexing `[T]` once results in the type `T`, and automatically insert obvious type hints to the expanded form, which reduces the cases where one has to add type hints manually\n    ```clojure\n    (require '[criterium.core :as cr])\n\n    (sa/def arr\n      (sa/into-array [[int]] (map (fn [i] (map (fn [j] (* i j)) (range 10))) (range 10)))\n\n    (cr/quick-bench (dotimes [i 10] (dotimes [j 10] (aget arr i j))))\n    ;; Evaluation count : 792 in 6 samples of 132 calls.\n    ;;              Execution time mean : 910.441562 µs\n    ;;     Execution time std-deviation : 170.924552 µs\n    ;;    Execution time lower quantile : 758.037129 µs ( 2.5%)\n    ;;    Execution time upper quantile : 1.151744 ms (97.5%)\n    ;;                    Overhead used : 8.143474 ns\n\n    ;; The above result is way too slow due to unrecognizable reflection\n    ;; To avoid this slowness, you'll need to add type hints yourself\n\n    (cr/quick-bench (dotimes [i 10] (dotimes [j 10] (aget ^ints (aget arr i) j))))\n    ;; Evaluation count : 4122636 in 6 samples of 687106 calls.\n    ;;              Execution time mean : 139.098679 ns\n    ;;     Execution time std-deviation : 2.387043 ns\n    ;;    Execution time lower quantile : 136.235737 ns ( 2.5%)\n    ;;    Execution time upper quantile : 142.183007 ns (97.5%)\n    ;;                    Overhead used : 8.143474 ns\n\n    ;; Using `sa/aget`, you can simply write as follows:\n\n    (cr/quick-bench (dotimes [i 10] (dotimes [j 10] (sa/aget arr i j))))\n    ;; Evaluation count : 5000448 in 6 samples of 833408 calls.\n    ;;              Execution time mean : 113.195074 ns\n    ;;     Execution time std-deviation : 4.641354 ns\n    ;;    Execution time lower quantile : 108.656324 ns ( 2.5%)\n    ;;    Execution time upper quantile : 119.427431 ns (97.5%)\n    ;;                    Overhead used : 8.143474 ns\n    ```\n\n\n### Type-related utilities\n\n`sweet-array` also provides several utilities that are useful for dealing with\narray types.\n\n#### `(type [T])`\n\nThe `sweet-array.core/type` macro is convenient to reify an array type object\nrepresented with an [array type descriptor](#array-type-notation):\n\n```clojure\n(require '[sweet-array.core :as sa])\n\n(sa/type [int]) ;=\u003e int/1\n(sa/type [String]) ;=\u003e java.lang.String/1\n(sa/type [[double]]) ;=\u003e double/2\n```\n\n\u003e [!NOTE]\n\u003e It is recommended to use the array class syntax instead of the `type` macro\n\u003e when using Clojure 1.12 or later.\n\n#### `(instance? [T] expr)`\n\nThe `sweet-array.core/instance?` macro is a predicate to check if a given value is\nof the specified array type:\n\n```clojure\n(sa/instance? [int] (sa/new [int] [1 2 3])) ;=\u003e true\n(sa/instance? [Object] (sa/new [int] [1 2 3])) ;=\u003e false\n(sa/instance? [String] \"foo\") ;=\u003e false\n```\n\n`(sa/instance? [T] expr)` is just syntactic sugar for `(instance? (sa/type [T]) expr)`.\n\n#### `(cast [T] expr)`\n\nThe `sweet-array.core/cast` macro is for coercing an expression to the specified\narray type. It's useful for resolving reflection warnings when some expression\ncannot be type-inferred:\n\n```clojure\n(defn make-array [n] (sa/new [int] n))\n\n(set! *warn-on-reflection* true)\n\n(sa/aget (make-array 3) 0)\n;; Reflection warning, ... - call to static method aget on clojure.lang.RT can't be resolved (argument types: unknown, int).\n;=\u003e 0\n\n(sa/aget (sa/cast [int] (make-array 3)) 0)\n;=\u003e 0\n```\n\nNote that `sa/cast` only has the compile-time effect, and does nothing else at runtime.\n\n#### `#sweet/tag [T]`\n\nFor those who want to radically eliminate cryptic array type hints (e.g. `^\"[I\"`\nand `^\"[Ljava.lang.String;\"`) from your code, `sweet-array` provides reader syntax\nthat can be used as a replacement for them.\n\nBy prefixing `#sweet/tag`, you can write an array type descriptor as a type hint:\n\n```clojure\n(defn ^#sweet/tag [String] select-randomly [^#sweet/tag [[String]] arr]\n  (sa/aget arr (rand-int (alength arr))))\n```\n\nThis code compiles without any reflection warning, just as with:\n\n```clojure\n(defn ^\"[Ljava.lang.String;\" select-randomly [^\"[[Ljava.lang.String;\" arr]\n  (sa/aget arr (rand-int (alength arr))))\n```\n\n\u003e [!NOTE]\n\u003e It is recommended to use the array class syntax instead of the `#sweet/tag`\n\u003e type hint when using Clojure 1.12 or later.\n\n## Array type notation\n\n`sweet-array` adopts what we call *array type descriptors* to denote array types\nthroughout the library. Following is the definition of `sweet-array`'s\narray type descriptors:\n\n```\n      \u003carray type descriptor\u003e ::= '[' + \u003ccomponent type\u003e + ']'\n                                | \u003carray type alias\u003e\n                                | \u003csymbolic array type\u003e\n\n             \u003ccomponent type\u003e ::= \u003cprimitive type name\u003e\n                                | \u003creference type name\u003e\n                                | \u003carray type descriptor\u003e\n\n        \u003cprimitive type name\u003e ::= \u003csymbol primitive type name\u003e\n                                | \u003ckeyword primitive type name\u003e\n\n \u003csymbol primitive type name\u003e ::= 'boolean'\n                                | 'byte'\n                                | 'char'\n                                | 'short'\n                                | 'int'\n                                | 'long'\n                                | 'float'\n                                | 'double'\n\n\u003ckeyword primitive type name\u003e ::= ':' + \u003csymbol primitive type name\u003e\n\n        \u003creference type name\u003e ::= any valid class or interface name\n\n           \u003carray type alias\u003e ::= \u003csymbol array type alias\u003e\n                                | \u003ckeyword array type alias\u003e\n\n    \u003csymbol array type alias\u003e ::= 'booleans'\n                                | 'bytes'\n                                | 'shorts'\n                                | 'ints'\n                                | 'longs'\n                                | 'floats'\n                                | 'doubles'\n                                | 'objects'\n\n   \u003ckeyword array type alias\u003e ::= ':' + \u003csymbol array type alias\u003e\n\n        \u003csymbolic array type\u003e ::= \u003csymbol primitive type name\u003e + '/' + \u003cnum of dimensions\u003e\n                                | \u003creference type name\u003e + '/' + \u003cnum of dimensions\u003e\n\n           \u003cnum of dimesions\u003e ::= '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'\n```\n\nAn array type descriptor `[T]` denotes an array whose component type is `T`.\nThe component type itself may be an array type. For instance, `[[T]]` denotes\nthe two-dimensional array type of `T`, `[[[T]]]` denotes the three-dimensional\narray type of `T`, and so on.\n\nBoth array type aliases (e.g. `ints` and `doubles`) and array class syntax introduced\nin Clojure 1.12 (e.g. `int/1` and `String/2`) may also be used as array type\ndescriptors. They are completely interchangeable with their corresponding array type\ndescriptor: `ints` and `int/1` are equivalent to `[int]`, and `[doubles]` and\n`double/2` are equivalent to `[[double]]`, and so on.\n\n## License\n\nCopyright © 2021 Shogo Ohta\n\nThis program and the accompanying materials are made available under the\nterms of the Eclipse Public License 2.0 which is available at\nhttp://www.eclipse.org/legal/epl-2.0.\n\nThis Source Code may also be made available under the following Secondary\nLicenses when the conditions for such availability set forth in the Eclipse\nPublic License, v. 2.0 are satisfied: GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or (at your\noption) any later version, with the GNU Classpath Exception which is available\nat https://www.gnu.org/software/classpath/license.html.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fathos%2Fsweet-array","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fathos%2Fsweet-array","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fathos%2Fsweet-array/lists"}