Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parametric type on only some of variants #11

Open
disberd opened this issue Aug 20, 2024 · 3 comments
Open

parametric type on only some of variants #11

disberd opened this issue Aug 20, 2024 · 3 comments
Labels
gotcha common gotchas

Comments

@disberd
Copy link

disberd commented Aug 20, 2024

Hello,

I was trying out the package and wanted to reproduce the very simple example in the README of DynamicSumTypes.jl to compare synthax/performance.

That example (slightly adapted) creates a sumtype like so:

using DynamicSumTypes
abstract type AbstractAT end
struct A{X}
    x::X
end
struct B
    y::Float64
end
@sumtype AT(A{Int}, B) <: AbstractAT

I tried creating something similar with Moshi doing this:

@data AT{X} begin
	struct A
		x::X
	end
	struct B
		y::Float64
	end
end

The problem with the Moshi version is that AT.B has no constructor, it seems a constructor for B is correctly generated only in the following two cases:

  • B has no fields
  • B has at least one field of type X

Is there any alternative way to create variants that do not use the parametric type but still have fields?

@Roger-luo Roger-luo added the gotcha common gotchas label Aug 20, 2024
@Roger-luo
Copy link
Owner

Hi, this is intentional because variants A and B are dynamic variants of AT, thus, the static type parameter should always be specified at construction. Your example is not equivalent; your DST example has specified the type parameter, while you did not specify the type parameter in Moshi.

TL;DR, you should specify the type parameter for GADTs at construction.

julia> AT.B{Int}(1.0)
Main.AT.Type{Int64}(Main.AT.var"##Storage#B"{Int64}(1.0))

This is the same in Rust, e.g

enum AT<T> {
    A(T),
    B(f64),
}

writing the following will cause an error

let x = AT::B(1.0);

gives

error[E0282]: type annotations needed for `AT<_>`
  --> src/ir/node2.rs:23:9
   |
23 |     let x = AT::B(1.0);
   |         ^   ---------- type must be known at this point
   |
help: consider giving `x` an explicit type, where the type for type parameter `T` is specified
   |
23 |     let x: AT<T> = AT::B(1.0);
   |          +++++++

the reason why a singleton can have a constructor is that, for a singleton, we can use UnionAll{} to mark a type parameter unspecified in Julia and infer what type parameter that should be later because the singleton is invariant at conversion.

We cannot do this for other variants because this is not always true for variants with fields and we cannot make the type parameter adaptive, e.g, what if my type parameters are annotating units

@data SomeUnitValue{Unit} begin
    UnitA(Float64)
    UnitB(Float64)
end

Then we don't know what to do when expecting SomeUnitValue{mm}.UnitA when having value SomeUnitValue{unknown}.UnitA(1.0), adapting the unknown field to mm type parameter is not necessarily correct.

@disberd
Copy link
Author

disberd commented Aug 21, 2024

Thanks for the very detailed explanation (and for the very nice package), it is clearer now.

I think it would be worth to add some example/explanation about this in the docs, what do you think? (unless it's already there somewhere and I did not find it while reading yesterday)

@Roger-luo
Copy link
Owner

Definitely! As you can see, the documentation still needs to be completed. (that's why I created the gotcha label, in case I forget)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
gotcha common gotchas
Projects
None yet
Development

No branches or pull requests

2 participants