Imitation of inheritance + mutable state with immutable objects

I had a problem modeling our domain with immutable objects.

Unrelated design

One basic feature of WObject (world object) and many features for implementing certain operations, such as OwnedObj (hp ​​/ owner / takeDamage), Movable (motionLeft / moveTo), Fighter (attack / attack).

At the end of the hierarchy, you have a mutable class that mixes with the corresponding attributes:

class Corvette(var position: Vect2) extends WObject with OwnedObj with Movable with Fighter

If the user wants to perform an operation (say, move a ship), follow these steps:

val opt = objects.collectFirst { case obj: Movable if obj.position == position => obj }
opt.fold(Log.error(s"Movable at $position not found!")) { obj =>
  obj.moveTo(position) // return type is Unit
}

Unrivaled design

If moveTo should return a new object, what type does it return?

trait Movable[Self <: Movable[Self]], Movable [_] , - . , Movable[_] with Fighter[_]? ?

Self bounds, :

def takeDamage(obj: OwnedObj): obj.Self = if (Random.nextDouble()) obj.takeDamage else obj.self

, ,

  def attackReachable(
    data: WObject.WorldObjUpdate[Self]
  ): WObject.WorldObjUpdate[data.value._2.Self]

.

, + , , .

:

case class WObject(position: Vect2, id: UUID=UUID.randomUUID())
case class OwnedObj(owner: Owner)
case class Movable(movementLeft: Int)
case class Fighter(attacked: Boolean)
case class Corvette(obj: WObject, owned: OwnedObj, movable: Movable, fighter: Fighter)

// Something that has both WObject and Movable
trait MovableOps[A <: ???] {
  def moveTo(obj: A, target: Vect2): A
}

, Corvette.

, .

, ?

val opt = objects.collectFirst { case obj: ??? if obj.position == position => obj }
opt.fold(Log.error(s"Movable at $position not found!")) { obj =>
  objects = objects - obj + obj.moveTo(position)
}

:)

:

+4
1

" _ " : (Movable[T] with Fighter[T] forSome {type T}).

attackReachable, . , "" . =:= Leibniz, , , , - . , , , :

def doSomething[T <: Moveable { type Self = T }](t: T): T =
  t.somethingThatReturnsTDotSelf()

, , , - ( Monocle, ):

trait Move[A] {
  val lens: Lens[A, (WObject, Movable)]
}
/** This could be implicitly derived with Generic if you really want to -
or you could use Records. */
implicit def moveCorvette = new Move[Corvette] {
  val lens = lens[Corvette].obj ~ lens[Corvette].movable
}

def moveTo[A: Move](obj: A, target: Vect2) = {
  val l = Lens[A, (Wobject, Movable)]
  val remainingMoves = l.get(obj)._2.movementLeft - 1
  l.set(obj)((target, remainingMoves))
}

, HList, (, Fighter :: Corvette :: HNil), (, trait ObjAndMove {type T; val obj: T; val evidence: Move[T]}, List[ObjAndMove])

+3

All Articles