assemblyscript icon indicating copy to clipboard operation
assemblyscript copied to clipboard

Map.get() always returns error, no way to catch it?

Open KieranP opened this issue 2 months ago • 6 comments

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?

KieranP avatar Oct 27 '25 03:10 KieranP

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)
}

KieranP avatar Oct 27 '25 04:10 KieranP

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.

HerrCai0907 avatar Oct 27 '25 08:10 HerrCai0907

Issues in play here:

  • A Map<K,i32> would need to return i32 | null, which is an invalid type.
  • A Map<K,V | null> would have an ambiguous null return.
  • 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.

dcodeIO avatar Oct 27 '25 14:10 dcodeIO

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.

mattjohnsonpint avatar Oct 27 '25 14:10 mattjohnsonpint

See https://github.com/tc39/proposal-upsert

Mopsgamer avatar Nov 11 '25 17:11 Mopsgamer

You can always use https://github.com/JairusSW/try-as in the meantime lol

JairusSW avatar Nov 18 '25 04:11 JairusSW