aeternity / aepp-calldata-js

Aeternity data serialization library
ISC License
3 stars 4 forks source link

Support custom Set implementation and aliases to Set #140

Closed davidyuk closed 1 year ago

davidyuk commented 2 years ago

Originally raised in https://github.com/aeternity/aepp-calldata-js/pull/127#discussion_r845009331

While this issue doesn't have a significant impact, this can very confuse a developer in the edge case.

As I understand, currently set type detected by name comparison: https://github.com/aeternity/aepp-calldata-js/blob/d2774ec7af75db6f0b4331d85d410b4e7c561488/src/TypeResolver.js#L108-L110, https://github.com/aeternity/aepp-calldata-js/blob/d2774ec7af75db6f0b4331d85d410b4e7c561488/src/TypeResolver.js#L217-L218 But Set.set can have a different definition, the same as other name can be aliased to Set.set. To solve this I'm proposing to assume every type defined as record <name>('a) = { to_map : map('a, unit) } (Set.aes) a set.

I've generated corresponding contracts and ACI:

include "Set.aes"
include "SetCustom.aes"
include "SetAlias.aes"

contract Identity =
     entrypoint test_set(a: Set.set(int)) = a
     entrypoint test_set_custom(a: SetCustom.set) = a
     entrypoint test_set_alias(a: SetAlias.set(int)) = a

SetCustom.aes

namespace SetCustom =
  type set = int

SetAlias.aes

namespace SetAlias =
  record set('a) = { to_map : map('a, unit) }
{
  "encodedAci": {
    "contract": {
      "functions": [
        {
          "arguments": [
            {
              "name": "a",
              "type": {
                "Set.set": [
                  "int"
                ]
              }
            }
          ],
          "name": "test_set",
          "payable": false,
          "returns": {
            "Set.set": [
              "int"
            ]
          },
          "stateful": false
        },
        {
          "arguments": [
            {
              "name": "a",
              "type": "SetCustom.set"
            }
          ],
          "name": "test_set_custom",
          "payable": false,
          "returns": "int",
          "stateful": false
        },
        {
          "arguments": [
            {
              "name": "a",
              "type": {
                "SetAlias.set": [
                  "int"
                ]
              }
            }
          ],
          "name": "test_set_alias",
          "payable": false,
          "returns": {
            "SetAlias.set": [
              "int"
            ]
          },
          "stateful": false
        }
      ],
      "kind": "contract_main",
      "name": "Identity",
      "payable": false,
      "type_defs": []
    }
  },
  "externalEncodedAci": [
    {
      "namespace": {
        "name": "ListInternal",
        "type_defs": []
      }
    },
    {
      "namespace": {
        "name": "List",
        "type_defs": []
      }
    },
    {
      "namespace": {
        "name": "Option",
        "type_defs": []
      }
    },
    {
      "namespace": {
        "name": "Pair",
        "type_defs": []
      }
    },
    {
      "namespace": {
        "name": "Set",
        "type_defs": [
          {
            "name": "set",
            "typedef": {
              "record": [
                {
                  "name": "to_map",
                  "type": {
                    "map": [
                      "'a",
                      "unit"
                    ]
                  }
                }
              ]
            },
            "vars": [
              {
                "name": "'a"
              }
            ]
          }
        ]
      }
    },
    {
      "namespace": {
        "name": "SetCustom",
        "type_defs": [
          {
            "name": "set",
            "typedef": "int",
            "vars": []
          }
        ]
      }
    },
    {
      "namespace": {
        "name": "SetAlias",
        "type_defs": [
          {
            "name": "set",
            "typedef": {
              "record": [
                {
                  "name": "to_map",
                  "type": {
                    "map": [
                      "'a",
                      "unit"
                    ]
                  }
                }
              ]
            },
            "vars": [
              {
                "name": "'a"
              }
            ]
          }
        ]
      }
    }
  ],
  "interface": "\n\n\n\nnamespace Set =\n  record set('a) = {to_map : map('a, unit)}\n\nnamespace SetCustom =\n  type set = int\n\nnamespace SetAlias =\n  record set('a) = {to_map : map('a, unit)}\n\nmain contract Identity =\n  entrypoint test_set : (Set.set(int)) => Set.set(int)\n  entrypoint test_set_custom : (SetCustom.set) => int\n  entrypoint test_set_alias : (SetAlias.set(int)) => SetAlias.set(int)\n"
}

The generated ACI seems to be enough to detect Set by type instead of name.

dincho commented 2 years ago

I've already tried that implementation, it does not fit the lib architecture. I'd rather ask the upstream (ACI implementation) to provide a way to distinguish it.

dincho commented 1 year ago

We had short chart with @radrow that led to a conclusion that std libs are indistinguishable from any other types/libs (There is no native Set type).

The mapping in this library is only for better DX, thus it's simple name based implementation, the proposed changed are de-facto reimplementing (hardcoding) the type definitions in this library. There are more types than the set as well.

Given the above, I'm closing this issue as wontfix, until some of the above changes.