temperlang / temper

3 stars 0 forks source link

C# multiple Listed/Mapped parameters edge case #71

Closed tjpalmer closed 1 month ago

tjpalmer commented 9 months ago

The C# backend translates Listed to IReadOnlyList and Mapped to IReadOnlyDictionary, but in .NET, IList isn't a subtype of IReadOnlyList, even though List is a subtype of both, and same issue for Dictionary types. We provide convenience and work around this today by making overloads for Listed to all 3 list types mentioned above, but we don't do exponential overloads. So for example, Temper like this:

let something(a: Mapped<String, Int>, b: Mapped<String, Int>): Int { ... }
let c = something(someMap(d), someMapBuilder(e));

Will fail at the C# compiler because we'll make IDictionary and IReadOnlyDictionary overloads for ALL parameters at a time, but we don't do all the combinations, because exponential can get big.

To handle the general case, we need the expected type of expressions in TmpL. For example, I know when a value is a MapBuilder, but I need to know if it's going into a Mapped. If so, I need to wrap/cast it in generated C# code to an IReadOnlyList.

tjpalmer commented 1 month ago

This is likely fixed since I pulled out the overloads and now use targeted type handling, but I hit a different problem while testing:

$ let something(a: Mapped<String, Int>, b: Mapped<String, Int>): Int { a.length + b.length }
interactive#0: void
$ something(new Map([new Pair("a", 1)]), new MapBuilder<String, Int>())
interactive#1: 1
$ translate(1, "csharp")
[interactive#1]: interactive#0 does not export symbol something
An operation is not implemented: External reference: nym`-repl/i0000//chunk.temper`.something at CSharpTranslator.kt:1655
$ translate(1, "py")
[interactive#1]: interactive#0 does not export symbol something
Translated py for interactive#1
  interactive-0001/
    interactive_0001/
      __init__.py: text/python
        import interactive_0001.chunk as _0
        from temper_core import await_safe_to_exit
        await_safe_to_exit()
      chunk.py: text/python
        from builtins import int
        from temper_core import Pair, map_constructor
        pair = Pair
        return_: 'int' = something(map_constructor((pair('a', 1),)), {})
        export = return_
      __init__.py.map: application/json
        { "version": 3, "file": "py/interactive-⋯": [], "names": [], "mappings": "A;A;A" }
      chunk.py.map: application/json
        { "version": 3, "file": "py/interactive-⋯C,EAAC,CAAC,CAAE,GAA6B,CAAC;AAAA,SAAAC" }
interactive#3: void
tjpalmer commented 1 month ago

Fixed the tangent bug in the previous comment, so now I can easily test this translation:

$ let something(a: Mapped<String, Int>, b: Mapped<String, Int>): Int { a.length + b.length }
interactive#0: void
$ something(new Map([new Pair("a", 1)]), new MapBuilder<String, Int>())
interactive#1: 1
$ translate(1, "csharp")
[interactive#1]: interactive#0 does not export symbol something
Translated csharp for interactive#1
  interactive-0001/
    src/
      Chunk/
        ChunkGlobal.cs: text/x-csharp
          using C0 = Interactive0001.Chunk;
          using C1 = TemperLang.Core;
          using G = System.Collections.Generic;
          namespace Interactive0001.Chunk
          {
              public static class ChunkGlobal
              {
                  internal static int return__7;
                  static ChunkGlobal()
                  {
                      return__7 = C0::ChunkGlobal.Something(C1::Mapped.ConstructMap(C1::Listed.CreateReadOnlyList<G::KeyValuePair<string, int>>(new G::KeyValuePair<string, int>("a", 1))), C1::Mapped.AsReadOnly(new C1::OrderedDictionary<string, int>()));
                  }
              }
          }
        ChunkGlobal.cs.map: application/json
          { "version": 3, "file": "csharp/interact⋯AAA7B,IAA6B,KAAC,AAArE;AAAA;AAAqE;AAAA" }
interactive#2: void

Note the Mapped.AsReadOnly call, which shows the new behavior and ought to work correctly.