From 6ba922be2853d26abf86664f953bc527d4a97065 Mon Sep 17 00:00:00 2001 From: Nicolas Farabegoli Date: Thu, 23 Oct 2025 17:33:40 +0200 Subject: [PATCH 1/7] refactor!: better separate `ShareDataOps` from `NeighborValuesOps` # Conflicts: # scafi3-core/src/main/scala/it/unibo/scafi/language/ShareDataOps.scala # scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala --- .../test/inline/load-from-scala-inline.yml | 10 +-- .../scala/it/unibo/scafi/test/Gradient.scala | 4 +- .../scafi/language/AggregateFoundation.scala | 1 - .../unibo/scafi/language/ShareDataOps.scala | 56 ------------ .../{utils => language}/SharedDataOps.scala | 6 +- .../language/xc/FieldBasedSharedData.scala | 88 ++++++++++--------- .../scafi/language/xc/NeighborValuesOps.scala | 26 ++++++ .../xc/calculus/ExchangeCalculus.scala | 7 +- .../scafi/abstractions/AggregateTests.scala | 2 +- .../foundation/AggregateFoundationMock.scala | 9 +- .../ExchangeCalculusSemanticsTests.scala | 12 +-- .../it/unibo/scafi/runtime/Programs.scala | 6 +- 12 files changed, 97 insertions(+), 130 deletions(-) rename scafi3-core/src/main/scala/it/unibo/scafi/{utils => language}/SharedDataOps.scala (81%) create mode 100644 scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala diff --git a/alchemist-incarnation-scafi3/src/test/resources/it/unibo/scafi/test/inline/load-from-scala-inline.yml b/alchemist-incarnation-scafi3/src/test/resources/it/unibo/scafi/test/inline/load-from-scala-inline.yml index 15d26758..cb4468b0 100644 --- a/alchemist-incarnation-scafi3/src/test/resources/it/unibo/scafi/test/inline/load-from-scala-inline.yml +++ b/alchemist-incarnation-scafi3/src/test/resources/it/unibo/scafi/test/inline/load-from-scala-inline.yml @@ -12,17 +12,17 @@ _pool: &program package it.unibo.scafi.test import it.unibo.scafi.alchemist.device.sensors.AlchemistEnvironmentVariables - import it.unibo.scafi.language.AggregateFoundation import it.unibo.scafi.language.fc.syntax.FieldCalculusSyntax - import it.unibo.scafi.libraries.FieldCalculusLibrary.share - import it.unibo.scafi.sensors.DistanceSensor - import it.unibo.scafi.sensors.DistanceSensor.senseDistance + import it.unibo.scafi.language.xc.calculus.ExchangeCalculus import it.unibo.scafi.libraries.All import it.unibo.scafi.libraries.All.given + import it.unibo.scafi.libraries.FieldCalculusLibrary.share import it.unibo.scafi.message.Codables.given + import it.unibo.scafi.sensors.DistanceSensor + import it.unibo.scafi.sensors.DistanceSensor.senseDistance object GradientInlined: - type Lang = AggregateFoundation { type DeviceId = Int } & FieldCalculusSyntax & DistanceSensor[Double] & + type Lang = ExchangeCalculus { type DeviceId = Int } & FieldCalculusSyntax & DistanceSensor[Double] & AlchemistEnvironmentVariables def gradientInlined(using Lang): Double = diff --git a/alchemist-incarnation-scafi3/src/test/scala/it/unibo/scafi/test/Gradient.scala b/alchemist-incarnation-scafi3/src/test/scala/it/unibo/scafi/test/Gradient.scala index 9ba2ddec..bf82d93a 100644 --- a/alchemist-incarnation-scafi3/src/test/scala/it/unibo/scafi/test/Gradient.scala +++ b/alchemist-incarnation-scafi3/src/test/scala/it/unibo/scafi/test/Gradient.scala @@ -1,8 +1,8 @@ package it.unibo.scafi.test import it.unibo.scafi.alchemist.device.sensors.AlchemistEnvironmentVariables -import it.unibo.scafi.language.AggregateFoundation import it.unibo.scafi.language.fc.syntax.FieldCalculusSyntax +import it.unibo.scafi.language.xc.calculus.ExchangeCalculus import it.unibo.scafi.libraries.All import it.unibo.scafi.libraries.All.given import it.unibo.scafi.libraries.FieldCalculusLibrary.share @@ -11,7 +11,7 @@ import it.unibo.scafi.sensors.DistanceSensor import it.unibo.scafi.sensors.DistanceSensor.senseDistance object Gradient: - type Lang = AggregateFoundation { type DeviceId = Int } & FieldCalculusSyntax & DistanceSensor[Double] & + type Lang = ExchangeCalculus { type DeviceId = Int } & FieldCalculusSyntax & DistanceSensor[Double] & AlchemistEnvironmentVariables def gradient(using Lang): Double = diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/language/AggregateFoundation.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/AggregateFoundation.scala index 24a1083b..98745b75 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/language/AggregateFoundation.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/language/AggregateFoundation.scala @@ -1,7 +1,6 @@ package it.unibo.scafi.language import it.unibo.scafi.collections.SafeIterable -import it.unibo.scafi.utils.SharedDataOps import cats.Applicative diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/language/ShareDataOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/ShareDataOps.scala index 8f9e5594..e69de29b 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/language/ShareDataOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/language/ShareDataOps.scala @@ -1,56 +0,0 @@ -package it.unibo.scafi.language - -import scala.collection.MapView - -/** - * This trait defines the operations that can be performed on [[SharedData]]. - * @tparam SharedData - * the type of the NeighboringValue - * @tparam DeviceId - * the type of the device id - */ -trait ShareDataOps[SharedData[_], DeviceId]: - - extension [Value](sharedData: SharedData[Value]) - - /** - * @return - * the default value of the [[SharedData]] - */ - def default: Value - - /** - * @return - * the values associated with the device ids that override the default value - */ - def values: MapView[DeviceId, Value] - - /** - * Maps the [[SharedData]] to a new one with the value corresponding to the given device id set to the given value. - * @param id - * the device id - * @param value - * the new value - * @return - * the new [[SharedData]] - */ - def set(id: DeviceId, value: Value): SharedData[Value] - - /** - * @param id - * the device id - * @return - * the value associated with the given device id, or the default value if the device id value is not overridden - */ - def get(id: DeviceId): Value = sharedData.values.getOrElse(id, sharedData.default) - - /** - * Alias for `get`. - * @param id - * the device id - * @return - * the value associated with the given device id, or the default value if the device id value is not overridden - */ - def apply(id: DeviceId): Value = sharedData.get(id) - end extension -end ShareDataOps diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/utils/SharedDataOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/SharedDataOps.scala similarity index 81% rename from scafi3-core/src/main/scala/it/unibo/scafi/utils/SharedDataOps.scala rename to scafi3-core/src/main/scala/it/unibo/scafi/language/SharedDataOps.scala index 0e283ca3..10033fd4 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/utils/SharedDataOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/language/SharedDataOps.scala @@ -1,4 +1,4 @@ -package it.unibo.scafi.utils +package it.unibo.scafi.language import it.unibo.scafi.collections.SafeIterable @@ -22,7 +22,3 @@ trait SharedDataOps[F[A] <: SafeIterable[A]]: * the value of the "self" node */ def onlySelf: A - - def mapValues[B](f: A => B): F[B] - - def alignedMap[B, C](other: F[B])(f: (A, B) => C): F[C] diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala index f86f5c02..30cea49b 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala @@ -1,11 +1,8 @@ package it.unibo.scafi.language.xc -import scala.collection.MapView - import it.unibo.scafi.collections.SafeIterable -import it.unibo.scafi.language.ShareDataOps +import it.unibo.scafi.language.SharedDataOps import it.unibo.scafi.language.xc.calculus.ExchangeCalculus -import it.unibo.scafi.utils.SharedDataOps import cats.Applicative @@ -14,12 +11,12 @@ import cats.Applicative */ trait FieldBasedSharedData: this: ExchangeCalculus => - override type SharedData[T] = Field[T] + override type SharedData[Value] = Field[Value] /** * A Field (NValue in https://doi.org/10.1016/j.jss.2024.111976) is a mapping from device ids to values of type T. For * devices not aligned with the current device, the default value is used. - * @param default + * @param defaultValue * the default value for unaligned devices * @param neighborValues * the values for all devices, aligned and unaligned @@ -27,8 +24,8 @@ trait FieldBasedSharedData: * the type of the values */ protected case class Field[+Value]( - default: Value, - neighborValues: Map[DeviceId, Value] = Map.empty, + private[xc] val defaultValue: Value, + private[xc] val neighborValues: Map[DeviceId, Value] = Map.empty, ) extends SafeIterable[Value]: /** @@ -36,12 +33,13 @@ trait FieldBasedSharedData: * a filtered view of the [[SharedData]] data that only contains the values for aligned devices */ def alignedValues: Map[DeviceId, Value] = - if neighborValues.isEmpty then Map(localId -> default) // self is always aligned, even if there are no neighbors + if neighborValues.isEmpty then + Map(localId -> defaultValue) // self is always aligned, even if there are no neighbors else if alignedDevices.size == neighborValues.size then neighborValues // all devices are aligned, there is no need to filter else alignedDevices - .map(id => id -> neighborValues.getOrElse(id, default)) + .map(id => id -> neighborValues.getOrElse(id, defaultValue)) .toMap // in all other cases, I need to filter based on the aligned devices /** @@ -50,12 +48,12 @@ trait FieldBasedSharedData: * @return * the value for the given device id, or the default value if the device is not aligned */ - def apply(id: DeviceId): Value = alignedValues.getOrElse(id, default) + def apply(id: DeviceId): Value = alignedValues.getOrElse(id, defaultValue) override def iterator: Iterator[Value] = alignedDevices - .map(id => neighborValues.getOrElse(id, default)) + .map(id => neighborValues.getOrElse(id, defaultValue)) .iterator - override def toString: String = s"Field($default, $neighborValues)" + override def toString: String = s"Field($defaultValue, $neighborValues)" end Field /** @@ -64,52 +62,56 @@ trait FieldBasedSharedData: */ protected def alignedDevices: Iterable[DeviceId] - override given fieldOps: ShareDataOps[SharedData, DeviceId] = new ShareDataOps[SharedData, DeviceId]: - extension [T](nv: Field[T]) - override def default: T = nv.default - override def values: MapView[DeviceId, T] = nv.alignedValues.view - override def set(id: DeviceId, value: T): SharedData[T] = Field[T]( - nv.default, - nv.neighborValues + (id -> value), - ) + override given neighborValuesOps: NeighborValuesOps[Field, DeviceId] = + new NeighborValuesOps[Field, DeviceId]: + extension [A](a: Field[A]) + override def mapValues[B](f: A => B): SharedData[B] = Field[B]( + f(a.default), + a.neighborValues.view.mapValues(f).toMap, + ) - override given sharedDataApplicative: Applicative[SharedData] = new Applicative[SharedData]: + override def alignedMap[B, C](other: SharedData[B])(f: (A, B) => C): SharedData[C] = + require( + a.neighborValues.keySet.diff(other.neighborValues.keySet).isEmpty, + s"Cannot alignedMap fields with different aligned devices: ${a.neighborValues.keySet} vs ${other.neighborValues.keySet}", + ) + Field[C]( + f(a.default, other.default), + a.neighborValues.view.map { case (id, value) => id -> f(value, other(id)) }.toMap, + ) + override def apply(id: DeviceId): A = a.neighborValues.getOrElse(id, a.defaultValue) + private[xc] override def set(id: DeviceId, value: A): SharedData[A] = Field[A]( + a.default, + a.neighborValues + (id -> value), + ) + override def default: A = a.defaultValue + override def neighbors: Map[DeviceId, A] = a.neighborValues.filterNot(_._1 == localId) + override def values: Map[DeviceId, A] = a.neighborValues + end extension + + override given sharedDataApplicative: Applicative[Field] = new Applicative[Field]: override def pure[A](x: A): Field[A] = Field(x, Map.empty) override def ap[A, B](ff: Field[A => B])(fa: Field[A]): Field[B] = Field( - ff.default(fa.default), + ff.defaultValue(fa.defaultValue), (ff.neighborValues.keySet ++ fa.neighborValues.keySet) .map(deviceId => deviceId -> ff(deviceId)(fa(deviceId))) .toMap, ) override def map[A, B](fa: Field[A])(f: A => B): Field[B] = Field[B]( - f(fa.default), + f(fa.defaultValue), fa.neighborValues.view.mapValues(f).toMap, ) - override given sharedDataOps: SharedDataOps[SharedData] = new SharedDataOps[SharedData]: + override given sharedDataOps: SharedDataOps[Field] = new SharedDataOps[Field]: extension [A](a: Field[A]) override def withoutSelf: SafeIterable[A] = - val filtered = a.alignedValues.view.filterKeys(_ != localId).values - SafeIterable(filtered) - override def onlySelf: A = a(localId) - - override def mapValues[B](f: A => B): SharedData[B] = Field[B]( - f(a.default), - a.neighborValues.view.mapValues(f).toMap, - ) - - override def alignedMap[B, C](other: SharedData[B])(f: (A, B) => C): SharedData[C] = - require( - a.neighborValues.keySet.diff(other.neighborValues.keySet).isEmpty, - s"Cannot alignedMap fields with different aligned devices: ${a.neighborValues.keySet} vs ${other.neighborValues.keySet}", + Field[A]( + a.defaultValue, + a.neighborValues - localId, ) - Field[C]( - f(a.default, other.default), - a.neighborValues.view.map { case (id, value) => id -> f(value, other(id)) }.toMap, - ) - end extension + override def onlySelf: A = a(localId) override given convert[T]: Conversion[T, SharedData[T]] = Field[T](_) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala new file mode 100644 index 00000000..d0421996 --- /dev/null +++ b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala @@ -0,0 +1,26 @@ +package it.unibo.scafi.language.xc + +/** + * This trait defines the operations that can be performed on NValues. + * @tparam SharedData + * the type of the NeighboringValue + * @tparam DeviceId + * the type of the device id + */ +trait NeighborValuesOps[SharedData[_], DeviceId]: + extension [A](sharedData: SharedData[A]) + def default: A + + def neighbors: Map[DeviceId, A] + + def mapValues[B](f: A => B): SharedData[B] + + def alignedMap[B, C](other: SharedData[B])(f: (A, B) => C): SharedData[C] + + def apply(id: DeviceId): A + + def values: Map[DeviceId, A] + + private[xc] def set(id: DeviceId, value: A): SharedData[A] + end extension +end NeighborValuesOps diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculus.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculus.scala index 825a8410..32c0b865 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculus.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculus.scala @@ -1,6 +1,7 @@ package it.unibo.scafi.language.xc.calculus -import it.unibo.scafi.language.{ AggregateFoundation, ShareDataOps } +import it.unibo.scafi.language.AggregateFoundation +import it.unibo.scafi.language.xc.NeighborValuesOps import it.unibo.scafi.message.CodableFromTo /** @@ -14,9 +15,9 @@ trait ExchangeCalculus extends AggregateFoundation: * @return * an instance of [[SharedDataOps]] * @see - * [[ShareDataOps]] + * [[NeighborValuesOps]] */ - given fieldOps: ShareDataOps[SharedData, DeviceId] = scala.compiletime.deferred + given neighborValuesOps: NeighborValuesOps[SharedData, DeviceId] = scala.compiletime.deferred /** * Local values can be considered [[SharedData]]. diff --git a/scafi3-core/src/test/scala/it/unibo/scafi/abstractions/AggregateTests.scala b/scafi3-core/src/test/scala/it/unibo/scafi/abstractions/AggregateTests.scala index bf4b0a54..7de006ae 100644 --- a/scafi3-core/src/test/scala/it/unibo/scafi/abstractions/AggregateTests.scala +++ b/scafi3-core/src/test/scala/it/unibo/scafi/abstractions/AggregateTests.scala @@ -2,7 +2,7 @@ package it.unibo.scafi.abstractions import it.unibo.scafi.UnitTest import it.unibo.scafi.collections.{ SafeIterable, SafeIterableTests } -import it.unibo.scafi.utils.SharedDataOps +import it.unibo.scafi.language.SharedDataOps trait AggregateTests: this: UnitTest & SafeIterableTests => diff --git a/scafi3-core/src/test/scala/it/unibo/scafi/language/foundation/AggregateFoundationMock.scala b/scafi3-core/src/test/scala/it/unibo/scafi/language/foundation/AggregateFoundationMock.scala index 52901157..ccb3e163 100644 --- a/scafi3-core/src/test/scala/it/unibo/scafi/language/foundation/AggregateFoundationMock.scala +++ b/scafi3-core/src/test/scala/it/unibo/scafi/language/foundation/AggregateFoundationMock.scala @@ -1,8 +1,7 @@ package it.unibo.scafi.language.foundation import it.unibo.scafi.collections.SafeIterable -import it.unibo.scafi.language.AggregateFoundation -import it.unibo.scafi.utils.SharedDataOps +import it.unibo.scafi.language.{ AggregateFoundation, SharedDataOps } import cats.Applicative @@ -18,9 +17,9 @@ class AggregateFoundationMock(deviceId: Int = 0) extends AggregateFoundation, Fi extension [A](a: MockAggregate[A]) override def withoutSelf: SafeIterable[A] = MockAggregate(a.mockedValues.tail) override def onlySelf: A = a.mockedValues.head - override def mapValues[B](f: A => B): MockAggregate[B] = MockAggregate(a.mockedValues.map(f)) - override def alignedMap[B, C](other: MockAggregate[B])(f: (A, B) => C): MockAggregate[C] = - MockAggregate(a.mockedValues.zip(other.mockedValues).map { case (x, y) => f(x, y) }) +// override def mapValues[B](f: A => B): MockAggregate[B] = MockAggregate(a.mockedValues.map(f)) +// override def alignedMap[B, C](other: MockAggregate[B])(f: (A, B) => C): MockAggregate[C] = +// MockAggregate(a.mockedValues.zip(other.mockedValues).map { case (x, y) => f(x, y) }) override given sharedDataApplicative: Applicative[MockAggregate] = new Applicative[MockAggregate]: override def pure[A](x: A): MockAggregate[A] = MockAggregate(Seq(x)) diff --git a/scafi3-core/src/test/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculusSemanticsTests.scala b/scafi3-core/src/test/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculusSemanticsTests.scala index 07b4e232..c8d370e4 100644 --- a/scafi3-core/src/test/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculusSemanticsTests.scala +++ b/scafi3-core/src/test/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculusSemanticsTests.scala @@ -16,23 +16,23 @@ trait ExchangeCalculusSemanticsTests: it should "provide the default value" in: nv.default shouldEqual 10 it should "allow to retrieve a value map" in: - val v: Map[lang.DeviceId, Int] = nv.values.toMap + val v: Map[lang.DeviceId, Int] = nv.neighbors.toMap v.values.toList should contain theSameElementsAs lang.device.toIterable .map(id => valuesMap.getOrElse(id, 10)) .toList it should "allow to retrieve a value" in: - nv.get(lang.localId) shouldEqual 1 nv(lang.localId) shouldEqual 1 - nv.get(lang.unalignedDeviceId) shouldEqual 10 + nv(lang.localId) shouldEqual 1 + nv(lang.unalignedDeviceId) shouldEqual 10 nv(lang.unalignedDeviceId) shouldEqual 10 it should "allow to override a value for an aligned device" in: val newNv = nv.set(lang.localId, 100) - newNv.get(lang.localId) shouldEqual 100 + newNv(lang.localId) shouldEqual 100 it should "not allow to override a value for an unaligned device" in: var newNv = nv.set(lang.localId, 100) - newNv.get(lang.unalignedDeviceId) shouldEqual 10 + newNv(lang.unalignedDeviceId) shouldEqual 10 newNv = newNv.set(lang.unalignedDeviceId, 100) - newNv.get(lang.unalignedDeviceId) shouldEqual 10 + newNv(lang.unalignedDeviceId) shouldEqual 10 end nvalues def exchangeCalculusSemanticsWithAtLeast10AlignedDevices[ diff --git a/scafi3-distributed/shared/src/test/scala/it/unibo/scafi/runtime/Programs.scala b/scafi3-distributed/shared/src/test/scala/it/unibo/scafi/runtime/Programs.scala index 7a0493cc..9714be60 100644 --- a/scafi3-distributed/shared/src/test/scala/it/unibo/scafi/runtime/Programs.scala +++ b/scafi3-distributed/shared/src/test/scala/it/unibo/scafi/runtime/Programs.scala @@ -34,7 +34,7 @@ trait Programs: ) def neighborsDiscoveryProgram: ProgramWithResult[Map[ID, Int]] = ProgramWithResult( - program = neighborValues(localId).neighborValues, + program = neighborValues(localId).values, expected = Map( 0 -> Map(1 -> 1, 2 -> 2, 0 -> 0), 1 -> Map(0 -> 0, 3 -> 3, 1 -> 1), @@ -45,9 +45,9 @@ trait Programs: def exchangeWithRestrictionsProgram: ProgramWithResult[Map[ID, Int]] = ProgramWithResult( program = branch(localId % 2 == 0)( - exchange(100)(returnSending).neighborValues, + exchange(100)(returnSending).values, )( - exchange(200)(returnSending).neighborValues, + exchange(200)(returnSending).values, ), expected = Map( 0 -> Map(2 -> 100, 0 -> 100), From 1ccc296fe360a42dcfc3d8dbc1e4a69598656996 Mon Sep 17 00:00:00 2001 From: Nicolas Farabegoli Date: Thu, 23 Oct 2025 18:10:29 +0200 Subject: [PATCH 2/7] refactor: better implementato for withoutSelf --- .../scafi/language/xc/FieldBasedSharedData.scala | 11 +++-------- .../unibo/scafi/language/xc/NeighborValuesOps.scala | 2 -- .../xc/calculus/ExchangeCalculusSemanticsTests.scala | 5 +++-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala index 30cea49b..93343026 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala @@ -85,7 +85,6 @@ trait FieldBasedSharedData: a.neighborValues + (id -> value), ) override def default: A = a.defaultValue - override def neighbors: Map[DeviceId, A] = a.neighborValues.filterNot(_._1 == localId) override def values: Map[DeviceId, A] = a.neighborValues end extension @@ -105,13 +104,9 @@ trait FieldBasedSharedData: ) override given sharedDataOps: SharedDataOps[Field] = new SharedDataOps[Field]: - extension [A](a: Field[A]) - override def withoutSelf: SafeIterable[A] = - Field[A]( - a.defaultValue, - a.neighborValues - localId, - ) - override def onlySelf: A = a(localId) + extension [A](field: Field[A]) + override def withoutSelf: SafeIterable[A] = SafeIterable(field.neighborValues.values) + override def onlySelf: A = field(localId) override given convert[T]: Conversion[T, SharedData[T]] = Field[T](_) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala index d0421996..05de3c8d 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala @@ -11,8 +11,6 @@ trait NeighborValuesOps[SharedData[_], DeviceId]: extension [A](sharedData: SharedData[A]) def default: A - def neighbors: Map[DeviceId, A] - def mapValues[B](f: A => B): SharedData[B] def alignedMap[B, C](other: SharedData[B])(f: (A, B) => C): SharedData[C] diff --git a/scafi3-core/src/test/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculusSemanticsTests.scala b/scafi3-core/src/test/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculusSemanticsTests.scala index c8d370e4..71503c70 100644 --- a/scafi3-core/src/test/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculusSemanticsTests.scala +++ b/scafi3-core/src/test/scala/it/unibo/scafi/language/xc/calculus/ExchangeCalculusSemanticsTests.scala @@ -1,6 +1,7 @@ package it.unibo.scafi.language.xc.calculus import it.unibo.scafi.UnitTest +import it.unibo.scafi.collections.SafeIterable trait ExchangeCalculusSemanticsTests: this: UnitTest => @@ -16,8 +17,8 @@ trait ExchangeCalculusSemanticsTests: it should "provide the default value" in: nv.default shouldEqual 10 it should "allow to retrieve a value map" in: - val v: Map[lang.DeviceId, Int] = nv.neighbors.toMap - v.values.toList should contain theSameElementsAs lang.device.toIterable + val v: SafeIterable[Int] = nv.withoutSelf + v.toList should contain theSameElementsAs lang.device.toIterable .map(id => valuesMap.getOrElse(id, 10)) .toList it should "allow to retrieve a value" in: From 99d6df118e1e9c52e4d6337b0040668492b55a7c Mon Sep 17 00:00:00 2001 From: Nicolas Farabegoli Date: Thu, 23 Oct 2025 18:17:24 +0200 Subject: [PATCH 3/7] feat: add get operator in NeighborValueOps and document methods --- .../language/xc/FieldBasedSharedData.scala | 1 + .../scafi/language/xc/NeighborValuesOps.scala | 53 ++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala index 93343026..84593b01 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala @@ -86,6 +86,7 @@ trait FieldBasedSharedData: ) override def default: A = a.defaultValue override def values: Map[DeviceId, A] = a.neighborValues + override def get(id: DeviceId): Option[A] = a.neighborValues.get(id) end extension override given sharedDataApplicative: Applicative[Field] = new Applicative[Field]: diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala index 05de3c8d..9e5e4a5d 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/NeighborValuesOps.scala @@ -3,20 +3,71 @@ package it.unibo.scafi.language.xc /** * This trait defines the operations that can be performed on NValues. * @tparam SharedData - * the type of the NeighboringValue + * the type of the SharedData. * @tparam DeviceId * the type of the device id */ trait NeighborValuesOps[SharedData[_], DeviceId]: extension [A](sharedData: SharedData[A]) + /** + * Returns the default value for unaligned devices. + * @return + * the default value. + */ def default: A + /** + * Maps the values of the SharedData using the provided function. + * @param f + * the mapping function. + * @tparam B + * the type of the mapped values. + * @return + * a new SharedData with the mapped values. + */ def mapValues[B](f: A => B): SharedData[B] + /** + * Maps the values of this SharedData and another SharedData using the provided function. Assumes both SharedData + * are aligned, i.e., they contain values for the same set of device ids. + * @param other + * the other SharedData to map with. + * @param f + * the mapping function that takes a value from this SharedData and a value from the other SharedData. + * @tparam B + * the type of the values in the other SharedData. + * @tparam C + * the type of the mapped values. + * @return + * a new SharedData with the mapped values. + */ def alignedMap[B, C](other: SharedData[B])(f: (A, B) => C): SharedData[C] + /** + * Retrieves the value associated with the given device id. If the device is unaligned, returns the default value. + * @param id + * the device id. + * @return + * the value associated with the device id. + * @throws NoSuchElementException + * if the device id is unaligned. + */ def apply(id: DeviceId): A + /** + * Retrieves the value associated with the given device id, if the device is aligned. + * @param id + * the device id. + * @return + * an option containing the value if the device is aligned, or None if it is unaligned. + */ + def get(id: DeviceId): Option[A] + + /** + * Retrieves a map of all device ids to their associated values. + * @return + * a map of device ids to values. + */ def values: Map[DeviceId, A] private[xc] def set(id: DeviceId, value: A): SharedData[A] From 9265423ecd287c837b19c7ecae682fc2d65ebb0b Mon Sep 17 00:00:00 2001 From: Nicolas Farabegoli Date: Fri, 24 Oct 2025 09:23:20 +0200 Subject: [PATCH 4/7] fix: provide extension methods only for neighbor values data --- .../scala/it/unibo/scafi/utils/NumericSharedDataOps.scala | 8 ++++---- .../it/unibo/scafi/utils/OrderingSharedDataOps.scala | 4 ++-- .../unibo/scafi/utils/PartialOrderingSharedDataOps.scala | 4 ++-- .../scala/it/unibo/scafi/utils/StringSharedDataOps.scala | 5 ++--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/utils/NumericSharedDataOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/utils/NumericSharedDataOps.scala index b85f8ba0..fa2fe442 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/utils/NumericSharedDataOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/utils/NumericSharedDataOps.scala @@ -1,9 +1,9 @@ package it.unibo.scafi.utils -import it.unibo.scafi.language.AggregateFoundation +import it.unibo.scafi.language.xc.FieldBasedSharedData object NumericSharedDataOps: - extension [V: Numeric as numeric](using lang: AggregateFoundation)(data: lang.SharedData[V]) + extension [V: Numeric as numeric](using lang: FieldBasedSharedData)(data: lang.SharedData[V]) infix def +(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(numeric.plus) infix def -(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(numeric.minus) infix def *(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(numeric.times) @@ -13,10 +13,10 @@ object NumericSharedDataOps: def toDouble: lang.SharedData[Double] = data.mapValues(numeric.toDouble) def abs: lang.SharedData[V] = data.mapValues(numeric.abs) - extension [V: Fractional as fractional](using lang: AggregateFoundation)(data: lang.SharedData[V]) + extension [V: Fractional as fractional](using lang: FieldBasedSharedData)(data: lang.SharedData[V]) infix def /(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(fractional.div) - extension [V: Integral as integral](using lang: AggregateFoundation)(data: lang.SharedData[V]) + extension [V: Integral as integral](using lang: FieldBasedSharedData)(data: lang.SharedData[V]) infix def /(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(integral.quot) infix def %(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(integral.rem) infix def /%(that: lang.SharedData[V]): lang.SharedData[(V, V)] = diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/utils/OrderingSharedDataOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/utils/OrderingSharedDataOps.scala index 2f8db2b5..6cb4c3f4 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/utils/OrderingSharedDataOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/utils/OrderingSharedDataOps.scala @@ -1,9 +1,9 @@ package it.unibo.scafi.utils -import it.unibo.scafi.language.AggregateFoundation +import it.unibo.scafi.language.xc.FieldBasedSharedData object OrderingSharedDataOps: - extension [V: Ordering as ordering](using lang: AggregateFoundation)(data: lang.SharedData[V]) + extension [V: Ordering as ordering](using lang: FieldBasedSharedData)(data: lang.SharedData[V]) infix def compare(that: lang.SharedData[V]): lang.SharedData[Int] = data.alignedMap(that)(ordering.compare) infix def min(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(ordering.min) infix def max(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(ordering.max) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/utils/PartialOrderingSharedDataOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/utils/PartialOrderingSharedDataOps.scala index cf663df7..3b5a7500 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/utils/PartialOrderingSharedDataOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/utils/PartialOrderingSharedDataOps.scala @@ -1,9 +1,9 @@ package it.unibo.scafi.utils -import it.unibo.scafi.language.AggregateFoundation +import it.unibo.scafi.language.xc.FieldBasedSharedData object PartialOrderingSharedDataOps: - extension [V: PartialOrdering as ordering](using lang: AggregateFoundation)(data: lang.SharedData[V]) + extension [V: PartialOrdering as ordering](using lang: FieldBasedSharedData)(data: lang.SharedData[V]) infix def <(that: lang.SharedData[V]): lang.SharedData[Boolean] = data.alignedMap(that)(ordering.lt) infix def <=(that: lang.SharedData[V]): lang.SharedData[Boolean] = data.alignedMap(that)(ordering.lteq) infix def >(that: lang.SharedData[V]): lang.SharedData[Boolean] = data.alignedMap(that)(ordering.gt) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/utils/StringSharedDataOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/utils/StringSharedDataOps.scala index 070d32db..8f279828 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/utils/StringSharedDataOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/utils/StringSharedDataOps.scala @@ -1,9 +1,9 @@ package it.unibo.scafi.utils -import it.unibo.scafi.language.AggregateFoundation +import it.unibo.scafi.language.xc.FieldBasedSharedData object StringSharedDataOps: - extension (using lang: AggregateFoundation)(data: lang.SharedData[String]) + extension (using lang: FieldBasedSharedData)(data: lang.SharedData[String]) infix def +(that: lang.SharedData[String]): lang.SharedData[String] = data.alignedMap(that)(_ + _) def toUpperCase: lang.SharedData[String] = data.mapValues(_.toUpperCase) def toLowerCase: lang.SharedData[String] = data.mapValues(_.toLowerCase) @@ -27,6 +27,5 @@ object StringSharedDataOps: def split(regex: String, limit: Int): lang.SharedData[Array[String]] = data.mapValues(_.split(regex, limit)) def toCharArray: lang.SharedData[Array[Char]] = data.mapValues(_.toCharArray) def reverse: lang.SharedData[String] = data.mapValues(_.reverse) - def mkString(sep: String): lang.SharedData[String] = data.mapValues(_.mkString(sep)) end extension end StringSharedDataOps From 4ef94565d78dd39c0ab0533b126abc5d5108fb8f Mon Sep 17 00:00:00 2001 From: Nicolas Farabegoli Date: Mon, 3 Nov 2025 14:26:02 +0100 Subject: [PATCH 5/7] chore: solve conflicts --- .../src/main/scala/it/unibo/scafi/language/ShareDataOps.scala | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 scafi3-core/src/main/scala/it/unibo/scafi/language/ShareDataOps.scala diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/language/ShareDataOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/ShareDataOps.scala deleted file mode 100644 index e69de29b..00000000 From 140390213bae2210601c0ac7c8d01ebe5e933fad Mon Sep 17 00:00:00 2001 From: Nicolas Farabegoli Date: Mon, 3 Nov 2025 14:28:49 +0100 Subject: [PATCH 6/7] refactor: rename lang to fieldData --- .../scafi/utils/NumericSharedDataOps.scala | 30 ++++++------- .../scafi/utils/OrderingSharedDataOps.scala | 9 ++-- .../utils/PartialOrderingSharedDataOps.scala | 15 ++++--- .../scafi/utils/StringSharedDataOps.scala | 45 ++++++++++--------- 4 files changed, 51 insertions(+), 48 deletions(-) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/utils/NumericSharedDataOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/utils/NumericSharedDataOps.scala index fa2fe442..daab935d 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/utils/NumericSharedDataOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/utils/NumericSharedDataOps.scala @@ -3,21 +3,21 @@ package it.unibo.scafi.utils import it.unibo.scafi.language.xc.FieldBasedSharedData object NumericSharedDataOps: - extension [V: Numeric as numeric](using lang: FieldBasedSharedData)(data: lang.SharedData[V]) - infix def +(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(numeric.plus) - infix def -(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(numeric.minus) - infix def *(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(numeric.times) - def unary_- : lang.SharedData[V] = data.mapValues(numeric.negate) - def toInt: lang.SharedData[Int] = data.mapValues(numeric.toInt) - def toLong: lang.SharedData[Long] = data.mapValues(numeric.toLong) - def toDouble: lang.SharedData[Double] = data.mapValues(numeric.toDouble) - def abs: lang.SharedData[V] = data.mapValues(numeric.abs) + extension [V: Numeric as numeric](using fieldData: FieldBasedSharedData)(data: fieldData.SharedData[V]) + infix def +(that: fieldData.SharedData[V]): fieldData.SharedData[V] = data.alignedMap(that)(numeric.plus) + infix def -(that: fieldData.SharedData[V]): fieldData.SharedData[V] = data.alignedMap(that)(numeric.minus) + infix def *(that: fieldData.SharedData[V]): fieldData.SharedData[V] = data.alignedMap(that)(numeric.times) + def unary_- : fieldData.SharedData[V] = data.mapValues(numeric.negate) + def toInt: fieldData.SharedData[Int] = data.mapValues(numeric.toInt) + def toLong: fieldData.SharedData[Long] = data.mapValues(numeric.toLong) + def toDouble: fieldData.SharedData[Double] = data.mapValues(numeric.toDouble) + def abs: fieldData.SharedData[V] = data.mapValues(numeric.abs) - extension [V: Fractional as fractional](using lang: FieldBasedSharedData)(data: lang.SharedData[V]) - infix def /(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(fractional.div) + extension [V: Fractional as fractional](using fieldData: FieldBasedSharedData)(data: fieldData.SharedData[V]) + infix def /(that: fieldData.SharedData[V]): fieldData.SharedData[V] = data.alignedMap(that)(fractional.div) - extension [V: Integral as integral](using lang: FieldBasedSharedData)(data: lang.SharedData[V]) - infix def /(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(integral.quot) - infix def %(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(integral.rem) - infix def /%(that: lang.SharedData[V]): lang.SharedData[(V, V)] = + extension [V: Integral as integral](using fieldData: FieldBasedSharedData)(data: fieldData.SharedData[V]) + infix def /(that: fieldData.SharedData[V]): fieldData.SharedData[V] = data.alignedMap(that)(integral.quot) + infix def %(that: fieldData.SharedData[V]): fieldData.SharedData[V] = data.alignedMap(that)(integral.rem) + infix def /%(that: fieldData.SharedData[V]): fieldData.SharedData[(V, V)] = data.alignedMap(that)((a, b) => (integral.quot(a, b), integral.rem(a, b))) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/utils/OrderingSharedDataOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/utils/OrderingSharedDataOps.scala index 6cb4c3f4..5460f89c 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/utils/OrderingSharedDataOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/utils/OrderingSharedDataOps.scala @@ -3,9 +3,10 @@ package it.unibo.scafi.utils import it.unibo.scafi.language.xc.FieldBasedSharedData object OrderingSharedDataOps: - extension [V: Ordering as ordering](using lang: FieldBasedSharedData)(data: lang.SharedData[V]) - infix def compare(that: lang.SharedData[V]): lang.SharedData[Int] = data.alignedMap(that)(ordering.compare) - infix def min(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(ordering.min) - infix def max(that: lang.SharedData[V]): lang.SharedData[V] = data.alignedMap(that)(ordering.max) + extension [V: Ordering as ordering](using fieldData: FieldBasedSharedData)(data: fieldData.SharedData[V]) + infix def compare(that: fieldData.SharedData[V]): fieldData.SharedData[Int] = + data.alignedMap(that)(ordering.compare) + infix def min(that: fieldData.SharedData[V]): fieldData.SharedData[V] = data.alignedMap(that)(ordering.min) + infix def max(that: fieldData.SharedData[V]): fieldData.SharedData[V] = data.alignedMap(that)(ordering.max) def max: V = data.withoutSelf.foldLeft(data.onlySelf)(ordering.max) def min: V = data.withoutSelf.foldLeft(data.onlySelf)(ordering.min) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/utils/PartialOrderingSharedDataOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/utils/PartialOrderingSharedDataOps.scala index 3b5a7500..17eb9e47 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/utils/PartialOrderingSharedDataOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/utils/PartialOrderingSharedDataOps.scala @@ -3,11 +3,12 @@ package it.unibo.scafi.utils import it.unibo.scafi.language.xc.FieldBasedSharedData object PartialOrderingSharedDataOps: - extension [V: PartialOrdering as ordering](using lang: FieldBasedSharedData)(data: lang.SharedData[V]) - infix def <(that: lang.SharedData[V]): lang.SharedData[Boolean] = data.alignedMap(that)(ordering.lt) - infix def <=(that: lang.SharedData[V]): lang.SharedData[Boolean] = data.alignedMap(that)(ordering.lteq) - infix def >(that: lang.SharedData[V]): lang.SharedData[Boolean] = data.alignedMap(that)(ordering.gt) - infix def >=(that: lang.SharedData[V]): lang.SharedData[Boolean] = data.alignedMap(that)(ordering.gteq) - infix def tryCompareTo(that: lang.SharedData[V]): lang.SharedData[Option[Int]] = + extension [V: PartialOrdering as ordering](using fieldData: FieldBasedSharedData)(data: fieldData.SharedData[V]) + infix def <(that: fieldData.SharedData[V]): fieldData.SharedData[Boolean] = data.alignedMap(that)(ordering.lt) + infix def <=(that: fieldData.SharedData[V]): fieldData.SharedData[Boolean] = data.alignedMap(that)(ordering.lteq) + infix def >(that: fieldData.SharedData[V]): fieldData.SharedData[Boolean] = data.alignedMap(that)(ordering.gt) + infix def >=(that: fieldData.SharedData[V]): fieldData.SharedData[Boolean] = data.alignedMap(that)(ordering.gteq) + infix def tryCompareTo(that: fieldData.SharedData[V]): fieldData.SharedData[Option[Int]] = data.alignedMap(that)(ordering.tryCompare) - infix def equiv(that: lang.SharedData[V]): lang.SharedData[Boolean] = data.alignedMap(that)(ordering.equiv) + infix def equiv(that: fieldData.SharedData[V]): fieldData.SharedData[Boolean] = + data.alignedMap(that)(ordering.equiv) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/utils/StringSharedDataOps.scala b/scafi3-core/src/main/scala/it/unibo/scafi/utils/StringSharedDataOps.scala index 8f279828..3fe3ea2d 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/utils/StringSharedDataOps.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/utils/StringSharedDataOps.scala @@ -3,29 +3,30 @@ package it.unibo.scafi.utils import it.unibo.scafi.language.xc.FieldBasedSharedData object StringSharedDataOps: - extension (using lang: FieldBasedSharedData)(data: lang.SharedData[String]) - infix def +(that: lang.SharedData[String]): lang.SharedData[String] = data.alignedMap(that)(_ + _) - def toUpperCase: lang.SharedData[String] = data.mapValues(_.toUpperCase) - def toLowerCase: lang.SharedData[String] = data.mapValues(_.toLowerCase) - def length: lang.SharedData[Int] = data.mapValues(_.length) - def trim: lang.SharedData[String] = data.mapValues(_.trim) - def strip: lang.SharedData[String] = data.mapValues(_.strip) - def stripLeading: lang.SharedData[String] = data.mapValues(_.stripLeading) - def stripTrailing: lang.SharedData[String] = data.mapValues(_.stripTrailing) - def isBlank: lang.SharedData[Boolean] = data.mapValues(_.isBlank) - def isEmpty: lang.SharedData[Boolean] = data.mapValues(_.isEmpty) - def charAt(index: Int): lang.SharedData[Char] = data.mapValues(_.charAt(index)) + extension (using lang: FieldBasedSharedData)(fieldData: lang.SharedData[String]) + infix def +(that: lang.SharedData[String]): lang.SharedData[String] = fieldData.alignedMap(that)(_ + _) + def toUpperCase: lang.SharedData[String] = fieldData.mapValues(_.toUpperCase) + def toLowerCase: lang.SharedData[String] = fieldData.mapValues(_.toLowerCase) + def length: lang.SharedData[Int] = fieldData.mapValues(_.length) + def trim: lang.SharedData[String] = fieldData.mapValues(_.trim) + def strip: lang.SharedData[String] = fieldData.mapValues(_.strip) + def stripLeading: lang.SharedData[String] = fieldData.mapValues(_.stripLeading) + def stripTrailing: lang.SharedData[String] = fieldData.mapValues(_.stripTrailing) + def isBlank: lang.SharedData[Boolean] = fieldData.mapValues(_.isBlank) + def isEmpty: lang.SharedData[Boolean] = fieldData.mapValues(_.isEmpty) + def charAt(index: Int): lang.SharedData[Char] = fieldData.mapValues(_.charAt(index)) def substring(beginIndex: Int, endIndex: Int): lang.SharedData[String] = - data.mapValues(_.substring(beginIndex, endIndex)) - def contains(seq: String): lang.SharedData[Boolean] = data.mapValues(_.contains(seq)) - def startsWith(prefix: String): lang.SharedData[Boolean] = data.mapValues(_.startsWith(prefix)) - def endsWith(suffix: String): lang.SharedData[Boolean] = data.mapValues(_.endsWith(suffix)) - def replace(oldChar: Char, newChar: Char): lang.SharedData[String] = data.mapValues(_.replace(oldChar, newChar)) + fieldData.mapValues(_.substring(beginIndex, endIndex)) + def contains(seq: String): lang.SharedData[Boolean] = fieldData.mapValues(_.contains(seq)) + def startsWith(prefix: String): lang.SharedData[Boolean] = fieldData.mapValues(_.startsWith(prefix)) + def endsWith(suffix: String): lang.SharedData[Boolean] = fieldData.mapValues(_.endsWith(suffix)) + def replace(oldChar: Char, newChar: Char): lang.SharedData[String] = + fieldData.mapValues(_.replace(oldChar, newChar)) def replaceAll(regex: String, replacement: String): lang.SharedData[String] = - data.mapValues(_.replaceAll(regex, replacement)) - def split(regex: String): lang.SharedData[Array[String]] = data.mapValues(_.split(regex)) - def split(regex: String, limit: Int): lang.SharedData[Array[String]] = data.mapValues(_.split(regex, limit)) - def toCharArray: lang.SharedData[Array[Char]] = data.mapValues(_.toCharArray) - def reverse: lang.SharedData[String] = data.mapValues(_.reverse) + fieldData.mapValues(_.replaceAll(regex, replacement)) + def split(regex: String): lang.SharedData[Array[String]] = fieldData.mapValues(_.split(regex)) + def split(regex: String, limit: Int): lang.SharedData[Array[String]] = fieldData.mapValues(_.split(regex, limit)) + def toCharArray: lang.SharedData[Array[Char]] = fieldData.mapValues(_.toCharArray) + def reverse: lang.SharedData[String] = fieldData.mapValues(_.reverse) end extension end StringSharedDataOps From 6d5922af721f9193405469bc54b79a0ccf393f37 Mon Sep 17 00:00:00 2001 From: Nicolas Farabegoli Date: Mon, 3 Nov 2025 14:31:20 +0100 Subject: [PATCH 7/7] style: remove comments --- .../scafi/language/foundation/AggregateFoundationMock.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/scafi3-core/src/test/scala/it/unibo/scafi/language/foundation/AggregateFoundationMock.scala b/scafi3-core/src/test/scala/it/unibo/scafi/language/foundation/AggregateFoundationMock.scala index ccb3e163..a198cb65 100644 --- a/scafi3-core/src/test/scala/it/unibo/scafi/language/foundation/AggregateFoundationMock.scala +++ b/scafi3-core/src/test/scala/it/unibo/scafi/language/foundation/AggregateFoundationMock.scala @@ -17,9 +17,6 @@ class AggregateFoundationMock(deviceId: Int = 0) extends AggregateFoundation, Fi extension [A](a: MockAggregate[A]) override def withoutSelf: SafeIterable[A] = MockAggregate(a.mockedValues.tail) override def onlySelf: A = a.mockedValues.head -// override def mapValues[B](f: A => B): MockAggregate[B] = MockAggregate(a.mockedValues.map(f)) -// override def alignedMap[B, C](other: MockAggregate[B])(f: (A, B) => C): MockAggregate[C] = -// MockAggregate(a.mockedValues.zip(other.mockedValues).map { case (x, y) => f(x, y) }) override given sharedDataApplicative: Applicative[MockAggregate] = new Applicative[MockAggregate]: override def pure[A](x: A): MockAggregate[A] = MockAggregate(Seq(x))