javascalaakka

Type mismatch in Akka Typed project


I got problem in UserActor, case SendMessageToRoom (20 line of the Actor):

Type mismatch. Required: UserCommand, found: RoomActor.BroadcastMessage

This application is mean to be a Chat. UserActor is an User, Room is a Chat. There is no Main class because it needs fix (doesn't influence on problem). Please, help me solve this problem and describe me what did I done incorrectly.

UserActor:

import Chat.RoomActor.RoomCommand
import akka.actor.typed.{ActorRef, Behavior}
import akka.actor.typed.scaladsl.Behaviors

import java.util.UUID

object UserActor {
  sealed trait UserCommand
  case class CreateUser(name: String, surname: String, age: Int, userActor: ActorRef[UserCommand]) extends UserCommand
  case class User(name: String, surname: String, age: Int, userId: String, userActor: ActorRef[UserCommand])
  case class SendMessageToRoom(room: ActorRef[RoomCommand], content: String, userName: String) extends UserCommand

  def apply(): Behavior[UserCommand] = userBehavior(Map.empty)

  private def userBehavior(users: Map[String, User]): Behavior[UserCommand] =
    Behaviors.receive{(context, message) =>
      message match {
       case SendMessageToRoom(room, content, userName) =>
          users.get(userName) match {
            case Some(user) =>
              user.userActor ! RoomActor.BroadcastMessage(user.userActor, content, room)
          }
          Behaviors.same
        case CreateUser(name, surname, age, userActor) =>
          val userId = UUID.randomUUID().toString
          val newUser = User(name, surname, age, userId, userActor)
          context.log.info(s"User $newUser added in user list")
          context.log.info(s"Created user: $userId")
          users + (userId -> newUser)
          Behaviors.same

        case _ =>
          context.log.warn("Unknown message for UserActor")
          Behaviors.same
      }
    }
}

RoomActor:

import Chat.UserActor.UserCommand
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.{ActorRef, Behavior}

object RoomActor {
  sealed trait RoomCommand

  case class CreateRoom(user: ActorRef[UserCommand], roomName: String) extends RoomCommand
  case class LeaveRoom(user: ActorRef[UserCommand], roomName: String) extends RoomCommand
  case class JoinRoom(user: ActorRef[UserCommand], roomName: String) extends RoomCommand
  case class BroadcastMessage(user: ActorRef[UserCommand], content: String, room: String) extends RoomCommand

  def apply(): Behavior[RoomCommand] = roomBehavior(Map.empty)

  private def roomBehavior(rooms: Map[String, ActorRef[RoomCommand]]): Behavior[RoomCommand] =
    Behaviors.receive { (context, message) =>
      message match {
        case CreateRoom(user, roomName) =>
          val newRoomActor = context.spawn(RoomActor(), roomName)
          context.log.info(s"$user created room: $roomName")
          roomBehavior(rooms + (roomName -> newRoomActor))
        case LeaveRoom(user, roomName) =>
          context.log.info(s"$user left room: $roomName")
          Behaviors.same
        case JoinRoom(user, roomName) =>
          context.log.info(s"$user joined room: $roomName")
          Behaviors.same

        case BroadcastMessage(user, content, room) =>
          context.log.info(s"Broadcast message from $user: $content")
          Behaviors.same

        case _ =>
          throw new IllegalArgumentException("Unknown message for RoomActor")
          Behaviors.same
      }
    }
}

I tried make "room" String. Doesn't help. Was another "Type mismatch" problem


Solution

  •        case SendMessageToRoom(room, content, userName) =>
              users.get(userName) match {
                case Some(user) =>
    -             user.userActor ! RoomActor.BroadcastMessage(user.userActor, content, room)
    +             room! RoomActor.BroadcastMessage(user.userActor, content, room)
              }
              Behaviors.same
    

    aa ! bb, that's means send the message "bb" to "aa" actor.

    Change your object-oriented way of thinking, This is not a method call.

    Treat each actor as a human being. On Typed Actor, the message type refers to the language that this person can recognize. ! means talk to this guy,

    In your case, the room actor is kind of like a room manager, he has a speaker, and when he receives a message from the person in the room, he will pick up the speaker, and then broadcast it.

    For this purpose, the room actor must explicitly maintain all users as a collection that who join this room and, upon receiving a broadcast message, broadcast the message through userCollection.foreach(u => u.actorRef ! message).

    Or you just have a typing mistake, I hope this answer solves your problem.