Map.get() always returns error, no way to catch it?
Question
I'm writing an AssemblyScript version of Conways Game of Life - https://github.com/KieranP/Game-Of-Life-Implementations
Part of it needs to fetch values from a Map but the key may not be there.
private cell_at(x: u32, y: u32): Cell {
return this.cells.get(`${x}-${y}`)
}
However, AssemblyScript always throws an error instead of returning null.
https://github.com/AssemblyScript/assemblyscript/blob/8257b1c60b6dc9a6a1395236b95a82efc261e3b8/std/assembly/map.ts#L103-L107
Map.find() is a private method, so I can't use that. And trying to use try/catch throws error ERROR AS100: Not implemented: Exceptions.
So how am I supposed to do this, try and fetch a map value, and handle when the map doesn't have the expected value?
According to the docs (https://www.assemblyscript.org/stdlib/map.html), it recommends using has first:
private cell_at(x: u32, y: u32): Cell | null {
const key = `${x}-${y}`
if (this.cells.has(key)) {
return this.cells.get(key)
} else {
return null
}
}
But that obviously adds 2x the function invocations, both using find.
It would be nice if Map.get allowed a second argument to specify the fallback value.
get(key: K, fallback: F): V {
let entry = this.find(key, HASH<K>(key));
if (!entry) {
if (fallback) return fallback
throw new Error(E_KEYNOTFOUND); // cannot represent `undefined`
}
return entry.value;
}
private cell_at(x: u32, y: u32): Cell | null {
return this.cells.get(`${x}-${y}`, null)
}
I agree with you. In the original design, I think AS just wants to avoid undefined because according to TS, here should be T | undefined instead of T | null.
@MaxGraey @dcodeIO @CountBleck Do you think we can return T | null for map.get? Though it is different with TS but it has better performance.
Issues in play here:
- A
Map<K,i32>would need to returni32 | null, which is an invalid type. - A
Map<K,V | null>would have an ambiguousnullreturn. - Not all (future) Wasm types might be nullable.
Providing a second argument to specify a default could work, but I think we'd need a separate method for it, say getWithDefault to not break the existing behavior.
I'd prefer a TryGet or MaybeGet pattern that gives both a boolean indicator of success and a value in the case of success.
A GetWithDefault pattern has the problem of not being able to distinguish between the default value existing at the key in the map, or the entry not existing at all.
See https://github.com/tc39/proposal-upsert
You can always use https://github.com/JairusSW/try-as in the meantime lol