what are some ways to avoid Obj.magic sprinkled all over your code? I always try hard to avoid this, but also always end up with Obj.magic all over the place in my rescript code bases.
as a habit, (and having worked in some other languages) I always put a SAFETY: comment above any black magic in my code (e.g., Obj.magic), but i’d still like to have less Obj.magic in my code. here are some examples just to show you how i’m using it:
// Load env files in order, applying overrides if enabled
paths->Array.forEach(path =>
switch Dotenv.config({path, processEnv: dict{}, quiet: true}) {
| {parsed, error: None} =>
if parsed->Dict.isEmpty {
warn(`File did not provide any env vars: ${path}`)
}
applyVars(parsed)
loadedFilesMut->Array.push(path)
| {error: Some(e), _} =>
// SAFETY: There's no scenario where this crashes hard. Worst case, code will just be None
let code = (Obj.magic(e)["code"] :> option<string>)
if code != Some("ENOENT") {
JsExn.throw(e)
}
}
above, e is a JsExn.t, which under certain circumstances can have a code field. what do?
here’s another snippet:
let v = Obj.magic(val)
let vt = typeof(v)
let tracked = switch vt {
| #object if v != Null.null =>
seen->Set.add(v)
true
| _ => false
}
let out = switch vt {
| #undefined => JSON.Encode.null
| #boolean => JSON.Encode.bool(v)
| #number => JSON.Encode.float(v)
| #bigint => encodeBigInt((v :> BigInt.t))
| #string => JSON.Encode.string(v)
| #symbol => encodeSymbol((v :> Symbol.t))
| #object if v == Null.null => JSON.Encode.null
| #object if Array.isArray(v) =>
could almost be done with a discriminated union, but not quite. ideas?
a third one:
let stringifySafe = v =>
JSON.stringifyAny(
v,
~replacer=Replacer(
(_, v) =>
switch typeof(v) {
| #bigint =>
let bi = (Obj.magic(v) :> BigInt.t)
JSON.Encode.string(BigInt.toString(bi) ++ "n")
| #symbol =>
let sym = (Obj.magic(v) :> Symbol.t)
JSON.Encode.string(sym->Symbol.toString)
| _ => v
},
),
)
come up with ideas or lecture me on this, i really dislike that i end up with all these Obj.magic sprinkled throughout