ocamlreason

how to populate existing list / array


I am new at reason / ocaml / functional programming.

I know about List.append and [] @ [] but these functions will create new list but how to populate existing list / array?

  1. What is the best way to populate list?
  2. What is the best way to populate array? Means if coords type is let coords: array point = [];
  3. Or this is wrong flow (algorithm) for such case?

Reason code:

type point = {x: int, y: int};

let coords: list point = [];

let append raw =>
  Array.iter
    (
      fun data => {
        let p = {x: data.x, y: data.y};
        /* how to append p to coords */
        ()
      }
    )
    raw;

JS analogue:

const coords = [];
const append = raw => raw.forEach({x, y} => {
  coords.push({
    x: process(x),
    y: process(y)
  });
});

Solution

  • Welcome to Reason!

    In Reason/OCaml, lists are immutable. Under the hood they're simple singly-linked list. You create new ones each time you "modify" them. Here's an example:

    let a = [1, 2, 3];
    let b = [0, ...a];
    

    This is akin to JavaScript's array "spread", except here you're taking the existing a, linking a new node 0 at the front, and calling it b. a still points to [1, 2, 3] (thus "immutable"). b is now [0, 1, 2, 3]. This is efficient since the [1, 2, 3] part is shared.

    The advantage of this is that you don't have to worry about passing your list around and accidentally have an obscure function modify it. List's immutability allows you to reason about your code purely by looking at the value you're staring right now (since it'll never change!).

    The drawback of list is that it's inefficient to add something at the end:

    let c = a @ [4] 
    

    That operation's basically taking a list of one item, [4], and successively attaching each item of [1, 2, 3] to it. So linear in terms of perf. But judging by the simplicity of the list implementation, it was historically deemed worth the tradeoff.

    So 3. it's the wrong flow if you're trying to set a list item.

    1. The best way to populate a list in your case is by mapping over it non-mutatively, from the old list: let newList = List.map (fun blabla => ...) raw
    2. Same for array. Map over it. There's Array.of_list and Array.to_list if you're ever stuck.

    More on array: OCaml array is mutable, and its size is unchangeable. Think of it as a block of memory. You'd allocate a new array through Array.make newSize, then populate it through Array.set. This wouldn't make sense if you're resizing the array a lot, so choose the right data structure.

    For JS compilation, BuckleScript compiles an ocaml array to a JS array. It's thus mutable and resizable. You'll find your familiar JS array operations under Js.Array

    As a general heuristic, if you'd like to change the length: try filter. If you'd like to change the length and the contained items, try fold_left. Otherwise, map.

    More recently, we've started implementing some immutable, resizable, optionally mutable array. Stay tuned!