Dotty 0.19 (Scala 3) features: contextual abstractions tl;dr 

I tried to write some as general and as feasible as possible code where entities are able to communicate with other entities of the same type or of different types, allowing for the recipient to reply to the sender.

Details :


Trying to work with some of the new contextual abstractions e.g. given and extension methods
Have a general Entity type to enable sending and receiving messages 
To be able to send message types different from what I can receive.
To have the ability to reply to a received message
To be able to send messages between 2 instances of the same type T given the presence of Entity[T, TMsg]
To be able to send messages between different types T and U given the presence of both Entity[T, TMsg] and Entity[U, UMsg]
Have a Person example capable of receiving Strings that can do all this


Concerns I have regarding the code :


Feels more complex, more difficult to understand than it needs to be
Type parameters are often just single character ones, but I felt like it would render the code totally unreadable, now however I have very long type signatures


The code

trait Entity[Me, InMsg] 
  def (me: Me) send[Receiver, OutMsg] (receiver: Receiver, msg: OutMsg) (given Entity[Receiver, OutMsg], Entity[Me, InMsg]): Unit
  def (me: Me) receive[Sender, ReplyMsg] (sender: Sender, msg: InMsg) (given Entity[Sender, ReplyMsg]): Unit
  def (me: Me) name(): String

class Person(private val name: String)

object Person

  given Entity[Person, String]

    def (me: Person) send[Receiver, OutMsg] (receiver: Receiver, msg: OutMsg) (given Entity[Receiver, OutMsg], Entity[Person, String]): Unit = 
      receiver.receive[Person, String](me, msg)

    def (me: Person) receive[Sender, ReplyMsg] (sender: Sender, msg: String) (given Entity[Sender, ReplyMsg]): Unit = 
      println(f"I'm ${me.name} and I just RECEIVED a msg saying $msg from ${sender.name()}")

    def (me: Person) name () = 
      me.name

@main def m = Person("AAA").send(Person("BBB"), "Hi")
First, a minor thing here:

def (me: Me) send[Receiver, OutMsg] (receiver: Receiver, msg: OutMsg) (given Entity[Receiver, OutMsg], Entity[Me, InMsg]): Unit


The second given parameter is actually redundant - it's literally the same type as  this, and with contextual parameters, there shouldn't be any other instance.

In fact, implementation of send in Person shows that you can have a default impl:

def (me: Me) send[Receiver, OutMsg] (receiver: Receiver, msg: OutMsg) (given Entity[Receiver, OutMsg]): Unit =
  receiver.receive(me, msg)(given this)




Next, something more subtle:

def (me: Me) receive[Sender, ReplyMsg] (sender: Sender, msg: InMsg) (given Entity[Sender, ReplyMsg]): Unit


Whoever is implementing this will have to support any ReplyMsg, and therefore won't be able to create one (without breaking type system by casting or nulls). Try implementing it in a way that sends something back - you won't be able to make a call to send compile with normal code. If it's desirable, you can simplify the signature using existentials:

def (me: Me) receive[Sender] (sender: Sender, msg: InMsg) (given Entity[Sender, ?]): Unit


But I think it's not, as you wanted to reply to things. You'll have to work out a way to make it work - the simplest one being having 3 parameters - Entity[Me, In, Out] and accepting something that has Me[Other, Out, In].