Skip to content

Instantly share code, notes, and snippets.

@bbqbaron
Created February 10, 2018 16:03
Show Gist options
  • Save bbqbaron/d5ad7647224394107b0c391c14050c14 to your computer and use it in GitHub Desktop.
Save bbqbaron/d5ad7647224394107b0c391c14050c14 to your computer and use it in GitHub Desktop.
Dump of some random Reason utility functions I found myself wanting
/***
* the util file I banged together while working on a project.
* as a result, it's organic, not systematic.
* some are so trivial as not to really need defining.
* i think i'm used to Clojure where a lack of currying
* means that sometimes aliasing trivial partial functions
* is helpful.
*/
/***
* classic "when". when predicate(value), call transform on value; otherwise return value
*/
let when_ = (predicate, transform, value) =>
if (predicate(value)) {
transform(value)
} else {
value
};
/***
* function composition. fn1 >> fn2 is arg => fn2(fn1(arg)).
* think of it as making a pipeline
*/
let (>>) = (fn1, fn2, arg) => fn2(fn1(arg));
/***
* force an optional to be present or crash.
* named after the ! operator in Kotlin/Swift,
* plus I like the name better than "orCrash"
*/
let bang =
fun
| Some(x) => x;
/***
* option mapping is so common that I created a top-level
* util function for it (since this isn't a systematic package :)).
* if value is present, call fn on it
*/
let omap = (fn, value) =>
switch value {
| Some(thing) => Some(fn(thing))
| _ => None
};
/***
* infix version of omap.
* as far as I know, you can't arbitrarily call functions as infix in Ocaml,
* as you can with `function` in other MLs.
* say it with me: "don't use infix operators!",
* but again, option-mapping is insanely common.
*/
let (>?) = (value, fn) => omap(fn, value);
/***
* unwrap options by providing a default value.
* default "value" to "default" if value is None.
*/
let or_ = (default, value) =>
switch value {
| Some(x) => x
| _ => default
};
/***
* infix version of or_.
* again, kind of wishing I could arbitrarily infix things!
*/
let (>~) = (value, default) => or_(default, value);
/***
* for debugging, print-and-return something preceded by a string tag,
* so you can inject it into the middle of chains of piped calls
*/
let debug = (tagName, value) => {
Js.log(tagName ++ ": ");
Js.log(value);
value
};
/***
* wrap something in a list
*/
let intoList = (x) => [x];
/***
* return what's passed. i'm sure this exists but i couldn't find it!
*/
let id = (x) => x;
/***
* swap the order of a function's arguments.
*/
let flip = (fn, a, b) => fn(b, a);
/***
* get an element at an index in a list,
* just a flip of List.nth to take the data structure last,
* as we're all used to.
*/
let idxUnsafe = flip(List.nth);
/***
* safe idxUnsafe, returning an option.
*/
let idx = (idx, list) =>
try (Some(List.nth(list, idx))) {
| Not_found => None
};
/***
* grab a random element from a list.
*/
let randNth = (list) => {
let idx = Js.Math.random_int(0, List.length(list));
List.nth(list, idx)
};
/***
* get the second element of a 2-tuple
*/
let snd = ((_, v)) => v;
/***
* wrap two values in a tuple,
* AKA Elm's ",", i think?
*/
let tup = (a, b) => (a, b);
/***
* convert a list of value to a list of (index, value)
*/
let pairs = (list) => List.mapi(tup, list);
/***
* get the second value of each of a list of tuples.
* boy, some of these are barely worth defining.
*/
let snds = (list) => List.map(snd, list);
/***
* get a random element from inside a list,
* returning it and the remaining list (that is, without the element).
* i didn't see "take" or "drop" in the standard lib, oddly, so this seemed relatively simple.
* unnecessarily slow since partition must iterate pointlessly over the entire list!
*/
let extractRand = (list: list('a)) : ('a, list('a)) => {
let randIdx = Js.Math.random_int(0, List.length(list));
let (toLeft, toRight) = list |> pairs |> List.partition(((i, _)) => i === randIdx);
(snd(List.hd(toLeft)), List.map(snd, toRight))
};
/***
* it doesn't seem like you can use type constructors
* as functions, so this function wraps something in Some()
*/
let some = (x) => Some(x);
/***
* remove Nones from a list of option,
* returning only good old-fashioned values
*/
let rec compress =
fun
| [] => []
| [Some(x), ...xs] => [x, ...compress(xs)]
| [None, ...xs] => compress(xs);
/***
* safe version of List.hd
*/
let hd =
fun
| [x, ..._] => Some(x)
| _ => None;
/***
* return a list of n calls to fn.
* calls fn each time, so presumably you want
* it to be effectful/nondeterministic/not slow.
*/
let repeat = (n, fn) => {
let rec _repeat = (n, acc) =>
switch n {
| 0 => acc
| x => _repeat(x - 1, [fn(), ...acc])
};
_repeat(n, [])
};
/***
* return whether any item in the list satisfies a predicate
*/
let any = (predicate, list) => {
let rec _any =
fun
| [] => false
| [x, ...rest] =>
if (predicate(x)) {
true
} else {
_any(rest)
};
_any(list)
};
/***
* i'm _very_ new at modules but thought i'd at least
* toy around with organizing some Dict-related utility functions
*/
module Dict = {
/***
* update a dict at a key with a function,
* which will be passed an option of the current value
* at that key.
*/
let update = (key, fn, dict) => Js.Dict.set(dict, key, fn(Js.Dict.get(dict, key)));
/***
* convert a Dict to a list of its values
*/
let vals = (dict) => dict |> Js.Dict.values |> Array.to_list;
/***
* construct a dict from a list of values,
* keying each entry by its index. i'm not positive why i wrote this, actually,
* since it's mostly just making a weird List.
* maybe an interop thing
*/
let ofItems = (list) =>
list |> List.mapi((idx, value) => (Js.Int.toString(idx), value)) |> Js.Dict.fromList;
};
/***
* sum a list of values
*/
let sum = List.fold_left((+), 0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment