Skip to content

Commit

Permalink
docs update, renaming functions
Browse files Browse the repository at this point in the history
  • Loading branch information
hunkyjimpjorps committed May 1, 2024
1 parent 8526353 commit c1a4a78
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 39 deletions.
12 changes: 8 additions & 4 deletions gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ version = "1.0.0"
# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.
#
# description = ""
# licences = ["Apache-2.0"]
# repository = { type = "github", user = "username", repo = "project" }
# links = [{ title = "Website", href = "https://gleam.run" }]
description = "Gleam interface to Erlang sparse arrays"
target = "erlang"
licences = ["Apache-2.0"]
repository = { type = "github", user = "hunkyjimpjorps", repo = "gary" }
internal_modules = [
"internal",
"internal/*"
]
#
# For a full reference of all the available options, you can have a look at
# https://gleam.run/writing-gleam/gleam-toml/.
Expand Down
166 changes: 136 additions & 30 deletions src/gary/array.gleam
Original file line number Diff line number Diff line change
@@ -1,119 +1,225 @@
//// Functional, extendible arrays, provided by Erlang's
//// [`array`](https://www.erlang.org/doc/man/array) module. Arrays can have fixed size,
//// or can grow automatically as needed ("extensible"). A default value is used for entries
//// that have not been explicitly set.
////
//// Erlang arrays are 0-indexed.
//// The maximum index for a fixed-size array of size `n` is `n - 1`.
//// There is no maximum index for a extensible array; the array resizes to fit as necessary.
//// Negative indices aren't supported.
////
//// Erlang doesn't distinguish between an unset entry and a entry that's been explicitly set
//// to the array's defined default value, which can cause issues when using functions that
//// drop unset entries like `sparse_map` or `sparse_to_list`. Use a type like `Option(t)` to
//// ensure that you can't accidentally cause this kind of collision.
////
//// See the Erlang docs for more information and caveats.
////

import gleam/bool
import gleam/dict.{type Dict}
import gleam/list
import internal/array_bindings.{type ErlangArray, Default}

pub type Array(t) =
ErlangArray(t)
pub type ArrayError {
IndexOutOfRange
BadSize
}

// create arrays
pub fn create(default: t) -> Array(t) {

/// Creates a new, extensible sparse array with a default value of `default` for any indices
/// without an assigned value.
pub fn create(default default: t) -> ErlangArray(t) {
array_bindings.new([#(Default, default)])
}

pub fn create_fixed_size(size size: Int, default default: t) -> Array(t) {
array_bindings.new_with_size(size, [#(Default, default)])
/// Creates a new, fixed-size sparse array of size `size` with a default value of `default`
/// for any indices without an assigned value. Returns a `BadSize` error if `size` is negative.
pub fn create_fixed_size(
size size: Int,
default default: t,
) -> Result(ErlangArray(t), ArrayError) {
use <- bool.guard(size < 0, Error(BadSize))
Ok(array_bindings.new_with_size(size, [#(Default, default)]))
}

pub fn create_from_list(from list: List(t), default default: t) -> Array(t) {
/// Converts a list to an extensible sparse array, assigning list members to indices
/// starting at an index of 0.
pub fn from_list(from list: List(t), default default: t) -> ErlangArray(t) {
array_bindings.from_list(list, default)
}

pub fn create_from_dict(from dict: Dict(Int, t), default default: t) -> Array(t) {
/// Converts a dict with integer keys representing array indices to an extensible sparse array.
/// Returns `IndexOutOfRange` if any of the keys are negative.
pub fn from_dict(
from dict: Dict(Int, t),
default default: t,
) -> Result(ErlangArray(t), ArrayError) {
use <- bool.guard(
list.any(dict.keys(dict), fn(k) { k < 0 }),
Error(IndexOutOfRange),
)
dict
|> dict.to_list
|> array_bindings.from_orddict(default)
|> Ok()
}

// create other types from arrays
pub fn to_list(array: Array(t)) -> List(t) {
/// Converts the array's values to a list, in order from index 0 to the maximum,
/// including the default value for any keys between 0 and
/// the maximum that don't have values.
pub fn to_list(array: ErlangArray(t)) -> List(t) {
array_bindings.to_list(array)
}

pub fn to_dict(array: Array(t)) -> Dict(Int, t) {
/// Converts the array to a dict with integer keys, including the default value for any keys
/// between 0 and the maximum that don't have values.
pub fn to_dict(array: ErlangArray(t)) -> Dict(Int, t) {
array
|> array_bindings.to_orddict()
|> dict.from_list
}

// modify array content
pub fn set(into array: Array(t), at index: Int, put item: t) -> Array(t) {
array_bindings.set(index, item, array)
/// Converts the array's values to a list, in order from lowest to highest index,
/// omitting default values.
pub fn to_list_without_defaults(array: ErlangArray(t)) -> List(t) {
array_bindings.sparse_to_list(array)
}

pub fn drop(from array: Array(t), at index: Int) -> Array(t) {
array_bindings.reset(index, array)
/// Converts the array to a dict with integer keys, omitting default values.
pub fn to_dict_without_defaults(array: ErlangArray(t)) -> Dict(Int, t) {
array
|> array_bindings.sparse_to_orddict()
|> dict.from_list
}

// modify array content
/// Returns a new array with `item` assigned to index `index`.
/// Returns `Error(IndexOutOfRange)` if the index is negative,
/// or if it exceeds the size of a fixed-size array.
pub fn set(
into array: ErlangArray(t),
at index: Int,
put item: t,
) -> Result(ErlangArray(t), ArrayError) {
use <- bool.guard(index <= 0, Error(IndexOutOfRange))
use <- bool.guard(
is_fixed_size(array) && index >= get_size(array),
Error(IndexOutOfRange),
)
Ok(array_bindings.set(index, item, array))
}

/// Returns a new array where the value at index `index` has been set to the array's default.
/// Returns `Error(IndexOutOfRange)` if the index is negative, or if it exceeds the
/// size of a fixed-size array.
pub fn drop(
from array: ErlangArray(t),
at index: Int,
) -> Result(ErlangArray(t), ArrayError) {
use <- bool.guard(index <= 0, Error(IndexOutOfRange))
use <- bool.guard(
is_fixed_size(array) && index >= get_size(array),
Error(IndexOutOfRange),
)
Ok(array_bindings.reset(index, array))
}

// higher-order array functions

pub fn map(over array: Array(a), with function: fn(Int, a) -> b) -> Array(b) {
/// Returns a new array that applies `function` to each array element, including default elements
/// within the current size of the array.
pub fn map(
over array: ErlangArray(a),
with function: fn(Int, a) -> b,
) -> ErlangArray(b) {
array_bindings.map(function, array)
}

pub fn sparse_map(
over array: Array(a),
over array: ErlangArray(a),
with function: fn(Int, a) -> b,
) -> Array(b) {
) -> ErlangArray(b) {
array_bindings.sparse_map(function, array)
}

pub fn fold(
over array: Array(a),
over array: ErlangArray(a),
from initial: b,
with function: fn(Int, a, b) -> b,
) -> b {
array_bindings.foldl(function, initial, array)
}

pub fn fold_right(
over array: Array(a),
over array: ErlangArray(a),
from initial: b,
with function: fn(Int, a, b) -> b,
) -> b {
array_bindings.foldr(function, initial, array)
}

pub fn sparse_fold(
over array: Array(a),
over array: ErlangArray(a),
from initial: b,
with function: fn(Int, a, b) -> b,
) -> b {
array_bindings.sparse_foldl(function, initial, array)
}

pub fn sparse_fold_right(
over array: Array(a),
over array: ErlangArray(a),
from initial: b,
with function: fn(Int, a, b) -> b,
) -> b {
array_bindings.sparse_foldr(function, initial, array)
}

// get array info
pub fn get(from array: Array(t), at index: Int) -> t {
array_bindings.get(index, array)
pub fn get(from array: ErlangArray(t), at index: Int) -> Result(t, ArrayError) {
use <- bool.guard(index <= 0, Error(IndexOutOfRange))
use <- bool.guard(
is_fixed_size(array) && index >= get_size(array),
Error(IndexOutOfRange),
)
Ok(array_bindings.get(index, array))
}

pub fn get_size(array: Array(t)) -> Int {
pub fn get_size(array: ErlangArray(t)) -> Int {
array_bindings.size(array)
}

pub fn get_default(array: Array(t)) -> t {
pub fn get_count(array: ErlangArray(t)) -> Int {
sparse_fold(array, 0, fn(_, _, acc) { acc + 1 })
}

pub fn get_default(array: ErlangArray(t)) -> t {
array_bindings.default(array)
}

pub fn is_fixed_size(array: Array(t)) -> Bool {
pub fn is_fixed_size(array: ErlangArray(t)) -> Bool {
array_bindings.is_fix(array)
}

// change the array's properties
pub fn fix_size(array: Array(t)) -> Array(t) {
pub fn make_fixed(array: ErlangArray(t)) -> ErlangArray(t) {
array_bindings.fix(array)
}

pub fn relax_size(array: Array(t)) -> Array(t) {
pub fn make_extensible(array: ErlangArray(t)) -> ErlangArray(t) {
array_bindings.relax(array)
}

pub fn set_size(array: Array(t), size: Int) -> Array(t) {
array_bindings.resize(size, array)
pub fn resize_to_fit(array: ErlangArray(t)) -> ErlangArray(t) {
array_bindings.resize(array)
}

pub fn set_size(
array: ErlangArray(t),
size: Int,
) -> Result(ErlangArray(t), ArrayError) {
use <- bool.guard(size < 0, Error(BadSize))
Ok(array_bindings.resize_to(size, array))
}
11 changes: 10 additions & 1 deletion src/internal/array_bindings.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ pub fn to_list(array: ErlangArray(t)) -> List(t)
@external(erlang, "array", "to_orddict")
pub fn to_orddict(array: ErlangArray(t)) -> List(#(Int, t))

@external(erlang, "array", "sparse_to_list")
pub fn sparse_to_list(array: ErlangArray(t)) -> List(t)

@external(erlang, "array", "sparse_to_orddict")
pub fn sparse_to_orddict(array: ErlangArray(t)) -> List(#(Int, t))

// get array info
@external(erlang, "array", "get")
pub fn get(index: Int, array: ErlangArray(t)) -> t
Expand Down Expand Up @@ -76,4 +82,7 @@ pub fn fix(array: ErlangArray(t)) -> ErlangArray(t)
pub fn relax(array: ErlangArray(t)) -> ErlangArray(t)

@external(erlang, "array", "resize")
pub fn resize(size: Int, array: ErlangArray(t)) -> ErlangArray(t)
pub fn resize_to(size: Int, array: ErlangArray(t)) -> ErlangArray(t)

@external(erlang, "array", "resize")
pub fn resize(array: ErlangArray(t)) -> ErlangArray(t)
Loading

0 comments on commit c1a4a78

Please sign in to comment.