Skip to content

Conversation

@linxuanm
Copy link
Contributor

@linxuanm linxuanm commented May 28, 2025

This PR experiments with adding Rust's trait system to Virgil to allow interface programming. The syntax adopted by this PR (still to be decided) is derived from doc/ideas/Traits.v3. A brief example:

// Represents a class that can be serialized into {T}
trait<A> Serializable<T> {
	def fromData(data: T) -> A;
	def toData(self: A) -> T;
}

impl Serializable<string> for i32 {
	def fromData(data: string) -> i32 { return 0; }
	def toData(self: i32) -> string { return "0"; }
}

class ConstStringBox<T>(value: T) {}

impl<T> Serializable<string> for ConstStringBox<T> {
	def fromData(data: string) -> ConstStringBox<T> { return ConstStringBox.new("foo"); }
	def toData(self: ConstStringBox<T>) -> string { return "bar"; }
}

Current progress on this PR (to my current understanding):

  • Parsing + basic syntax wellformedness
  • Implementation coverage/method conflict checks
  • Restricted type parameters (e.g., class Foo<T: MyTrait> { ... })
  • Error on overlapping patterns
  • Trait matching
  • Template instantiation
  • (TBD) Compound type parameter restriction? (e.g., T : TraitA & TraitB)

Please let me know your thoughts on this PR. Thanks!

@mkhan45
Copy link
Contributor

mkhan45 commented Jul 3, 2025

It might also make sense to implement type classes with components? e.g.

trait<A> Serializable<T> {
	def fromData(data: T) -> A;
	def toData(self: A) -> T;
}

component I32SerializeImplA {
	def fromData(data: string) -> i32 { return 0; }
	def toData(self: i32) -> string { return "0"; }
}

component I32SerializeImplB {
	def fromData(data: string) -> i32 { return 1; }
	def toData(self: i32) -> string { return "1"; }
}

def needs_serializable<T>(x: T, serialize: impl Serializeable<T>) { ... }
def main() {
  def x: i32 = 1;
  needs_serializable(x, I32SerializeImplA);
}

This is most similar to Scala implicits or OCaml functors. The trade off is that you have to either explicitly pass around the implementation or infer it, which might suck, but having the ability to define multiple implementations is often useful. This approach seems to fit into Virgil pretty well though since passing around components seems pretty natural.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants