Is there a way to mark a mutable type like an Js.Array2 as constant per the example below? Apologies if it is obvious, but I could not find any information on this.
let i_promise_to_not_modify = v => {
let _ = v->Js.Array2.push(1)
}
let run = () => {
let v = []
let _ = v->Js.Array2.push(1)
i_promise_to_not_modify(v)
Js.log(v)
}
run()
Arrays are always mutable, but you can define a type alias for an array and use a module interface to control how other modules are allowed to use it.
module M: {
type t
let make: int => t
} = {
type t = array<int>
let make = i => [i]
}
let a = M.make(1)
Js.Array2.push(a, 2) // ERROR!
Inside module M, the compiler can see that type t is just an array, so you can use any array functions on it. Outside of module M, the compiler only knows what the module’s interface exposes. The fact that t is an array is hidden, so, unless you expose a function like push, then it will be impossible for outside code to mutate the array.
I see. I can either define the make function as taking an array of ints and assume the caller did not leak a reference to the array (would this be zero cost?), or I can defensively copy the array when creating the typed version of the variable.
I see what you mean now. Yes, that sounds about right.
You could make it zero-cost by using external, but not if you want to defensively copy the array.
module M: {
type t
let make: array<int> => t
external make_zero_cost: array<int> => t = "%identity"
let make_copy: array<int> => t
} = {
type t = array<int>
let make = a => a
external make_zero_cost: array<int> => t = "%identity"
let make_copy = a => Js.Array2.copy(a)
}
As long as you expose the external in the interface, then it will compile away in the JS output.
Is it currently possible somehow in the language, no matter how technical (macro, ppx, etc.), to determine automatically if the call site contains a static expression (i.e. array literal) and pick the compiler output based on that determination?