tact icon indicating copy to clipboard operation
tact copied to clipboard

Support for `Either` type in tact

Open 0kenx opened this issue 1 year ago • 10 comments

Tact should add support for Either TL-B type.

I propose we use the ? operator: TL-B Either X Y -> Tact X ? Y. If it's too confusing we could also use a more explicit syntax Either<X, Y>.

0kenx avatar Jun 05 '24 14:06 0kenx

Yes, this is needed to implement #322. I'd propose to use the pipe symbol | instead of ? because it's essentially a type analogue of logical disjunction and there won't be grammar conflicts with the optional type operator: https://github.com/tact-lang/tact/blob/da6a7f6e3eb56a20f5daafa84e50d095f923f3ba/src/grammar/grammar.ohm#L24.

We should also outline the concrete syntax for the constructors and destructors for the Either type.

To construct an expression of an Either type, we need two constructors, we can reuse the Left and Right names that TL-B borrows from Haskell, because it's more general than what, for instance, Rust does with Ok and Error.

To destruct an expression of an Either type, we need pattern-matching:

let foo: X | Y = ...;
match foo {
  Left(l) => {
    return 42;
  }
  Right(r) => {
    return 24;
  }
}

or, in Ohm.js-like notation:

StatementMatch ::= "match" Expression "{" ListOf<MatchClause, ","> ","? "}"

MatchClause ::= Pattern "=>" "{" Statement* "}"

// patterns can be nested
Pattern ::= Constructor ("(" NonemptyListOf<Pattern,","> ","? ")")?
        | TactIdentifier
        | "_"

...

This could be good enough for an initial implementation, later we could support things like | for grouping patterns, etc. The initial implementation does not have to support the general algebraic data types, just the Either type.

anton-trunov avatar Jun 05 '24 15:06 anton-trunov

The only problem I have with using | is that, in many languages with sum types, | is either straight away commutative, or implementation details are hidden well enough so developers can treat it as commutative.

TL-B on the other hand, makes a clear distinction with the selector bit. And because we want Tact code to interoperate with TL-B definitions, there's no way to abstract that implementation detail away.

0kenx avatar Jun 06 '24 00:06 0kenx

Well, because it is commutative. In the sense that you can always build an isomorphism between Either X Y and Either Y X.

anton-trunov avatar Jun 06 '24 03:06 anton-trunov

Yup, but serializing type X in Either X Y would result in 0b0xxxxxxxx, and serializing X in Either Y X would result in 0b1xxxxxxxx. If people take the type from FunC and change the order without realizing what is under the hood it would lead to errors.

0kenx avatar Jun 06 '24 10:06 0kenx

Yeah, that's a good point, but still more verbose syntax won't prevent this. I'm happy to change my opinion if you have a convincing concrete example, though

anton-trunov avatar Jun 07 '24 04:06 anton-trunov

We may come up with an or keyword (as in Either ... or ...). It won't describe the layout, but it also won't be confused with general ADTs (not nearly as much as | would).

let foo: X or Y = ...;

As a bonus it pairs nicely with as serialization modifier, introducing a stronger visual bond between left and right parts as opposed to | or comma in Either<..., ...>

More examples:

// Maybe of Eithers'
let foo: (X or Y)? = ...;

// Alternative Maybe of Eithers'
let foo2: X or? Y = ...;

// Either of Maybes'
let bar: X? or Y? = ...;

// Throwing as into the mix
let baz: (X as uint64 or Y as coins)? = ...;
let baz2: X as uint64 or? Y as coins = ...; // alternative maybe of eithers'

novusnota avatar Jun 07 '24 11:06 novusnota

// Alternative Maybe of Eithers' let foo2: X or? Y = ...;

@novusnota what’s that in TL-B notation?

Gusarich avatar Jun 08 '24 08:06 Gusarich

@Gusarich Something like this, I guess:

// TL-B: Either X Y
let ex1: X or Y = ...; // just the Either

// TL-B: Maybe (Either X Y))
let ex2: X or? Y = ...; // Maybe of Either's, like here:
                        // https://github.com/ton-blockchain/ton/blob/140320b0dbe0bdd9fd954b6633a3677acc75a8e6/crypto/block/block.tlb#L155-L157

// TL-B: Either (Maybe X) (Maybe Y)
let ex3: X? or Y? = ...; // Either of Maybe's

// TL-B: Maybe (Either (Maybe X) (Maybe Y))
let ex4: X? or? Y? = ...; // Maybe of Either of Maybe's (inception!)

novusnota avatar Aug 24 '24 14:08 novusnota

@novusnota We should just allow ? to be used for any type, not only type idents, hence X or? Y becomes (X or Y)?

anton-trunov avatar Aug 25 '24 08:08 anton-trunov

I tried to implement this, but I came across the fact that in the type ABITypeRef from @ton/core there is no way to define either type.

Gusarich avatar Aug 27 '24 10:08 Gusarich

Just remembered that Kotlin (the language we name in the first commandment of Tact) uses as for type casts. See "unsafe" cast operator and "safe" nullable cast operator in their docs.

as and as? type casts performed

I'm not saying that we should have type casts (we probably don't, except for our !! operator), but the general idea of as? looks reminiscent of my ideas of or? syntax prior.

But this is also fine:

We should just allow ? to be used for any type, not only type idents, hence X or? Y becomes (X or Y)?

novusnota avatar Nov 25 '24 11:11 novusnota

Yes, obviously unsafe type casts have no place in security/finance applications.

Maybe and Either types are boolean-blind and exist in functional programming languages mostly for implementation of generic libraries that do not care about semantics of stored values. Contracts are not generic, do care about semantics, and shouldn't use either Either or Maybe.

I understand how adding Either might sound simpler than full-fledged algebraic data types, but winging it here will degrade code quality of the whole Tact ecosystem in the long run, and in my opinion just isn't worth it.

verytactical avatar Nov 25 '24 12:11 verytactical

@verytactical but the thing is that token standards have Either in their message schemas so we have to give a good way to define such structs.

Maybe we should add some flag for that, so that it's possible to strictly check which of the Either options was used during parsing?

Gusarich avatar Nov 27 '24 10:11 Gusarich