@@ -153,35 +153,29 @@ object AtomicCell {
153153 of(M .empty)(F )
154154 }
155155
156- private [effect] def async [F [_], A ](init : A )( implicit F : Async [ F ]) : F [ AtomicCell [ F , A ]] =
157- Mutex .apply[ F ].map(mutex => new AsyncImpl ( init, mutex))
158-
159- private [effect] def concurrent [ F [_], A ]( init : A )(
160- implicit F : Concurrent [ F ] ): F [AtomicCell [F , A ]] =
161- ( Ref .of[ F , A ](init), Mutex .apply[F ]).mapN { (ref, m) => new ConcurrentImpl (ref, m) }
156+ private [effect] def async [F [_], A ](
157+ init : A
158+ )(
159+ implicit F : Async [ F ]
160+ ): F [AtomicCell [F , A ]] =
161+ Mutex .apply[F ].map(mutex => new AsyncImpl (init, lock = mutex.lock))
162162
163- private final class ConcurrentImpl [F [_], A ](
164- ref : Ref [F , A ],
165- mutex : Mutex [F ]
163+ private [effect] def concurrent [F [_], A ](
164+ init : A
166165 )(
167166 implicit F : Concurrent [F ]
168- ) extends AtomicCell [F , A ] {
169- override def get : F [A ] = ref.get
170-
171- override def set (a : A ): F [Unit ] =
172- mutex.lock.surround(ref.set(a))
167+ ): F [AtomicCell [F , A ]] =
168+ (Ref .of[F , A ](init), Mutex .apply[F ]).mapN { (ref, mutex) =>
169+ new ConcurrentImpl (ref, lock = mutex.lock)
170+ }
173171
172+ // Provides common implementations for derived methods that depend on F being an applicative.
173+ private [effect] sealed abstract class CommonImpl [F [_], A ](
174+ implicit F : Applicative [F ]
175+ ) extends AtomicCell [F , A ] {
174176 override def modify [B ](f : A => (A , B )): F [B ] =
175177 evalModify(a => F .pure(f(a)))
176178
177- override def evalModify [B ](f : A => F [(A , B )]): F [B ] =
178- mutex.lock.surround {
179- ref.get.flatMap(f).flatMap {
180- case (a, b) =>
181- ref.set(a).as(b)
182- }
183- }
184-
185179 override def evalUpdate (f : A => F [A ]): F [Unit ] =
186180 evalModify(a => f(a).map(aa => (aa, ())))
187181
@@ -192,12 +186,33 @@ object AtomicCell {
192186 evalModify(a => f(a).map(aa => (aa, aa)))
193187 }
194188
195- private final class AsyncImpl [F [_], A ](
189+ private [effect] final class ConcurrentImpl [F [_], A ](
190+ ref : Ref [F , A ],
191+ lock : Resource [F , Unit ]
192+ )(
193+ implicit F : Concurrent [F ]
194+ ) extends CommonImpl [F , A ] {
195+ override def get : F [A ] =
196+ ref.get
197+
198+ override def set (a : A ): F [Unit ] =
199+ lock.surround(ref.set(a))
200+
201+ override def evalModify [B ](f : A => F [(A , B )]): F [B ] =
202+ lock.surround {
203+ ref.get.flatMap(f).flatMap {
204+ case (a, b) =>
205+ ref.set(a).as(b)
206+ }
207+ }
208+ }
209+
210+ private [effect] final class AsyncImpl [F [_], A ](
196211 init : A ,
197- mutex : Mutex [ F ]
212+ lock : Resource [ F , Unit ]
198213 )(
199214 implicit F : Async [F ]
200- ) extends AtomicCell [F , A ] {
215+ ) extends CommonImpl [F , A ] {
201216 @ volatile private var cell : A = init
202217
203218 override def get : F [A ] =
@@ -206,17 +221,14 @@ object AtomicCell {
206221 }
207222
208223 override def set (a : A ): F [Unit ] =
209- mutex. lock.surround {
224+ lock.surround {
210225 F .delay {
211226 cell = a
212227 }
213228 }
214229
215- override def modify [B ](f : A => (A , B )): F [B ] =
216- evalModify(a => F .pure(f(a)))
217-
218230 override def evalModify [B ](f : A => F [(A , B )]): F [B ] =
219- mutex. lock.surround {
231+ lock.surround {
220232 F .delay(cell).flatMap(f).flatMap {
221233 case (a, b) =>
222234 F .delay {
@@ -225,14 +237,93 @@ object AtomicCell {
225237 }
226238 }
227239 }
240+ }
228241
229- override def evalUpdate (f : A => F [A ]): F [Unit ] =
230- evalModify(a => f(a).map(aa => (aa, ())))
242+ /**
243+ * Allows seeing a `AtomicCell[F, Option[A]]` as a `AtomicCell[F, A]`. This is useful not only
244+ * for ergonomic reasons, but because some implementations may save space.
245+ *
246+ * Setting the `default` value is the same as storing a `None` in the underlying `AtomicCell`.
247+ */
248+ def defaultedAtomicCell [F [_], A ](
249+ atomicCell : AtomicCell [F , Option [A ]],
250+ default : A
251+ )(
252+ implicit F : Applicative [F ]
253+ ): AtomicCell [F , A ] =
254+ new DefaultedAtomicCell [F , A ](atomicCell, default)
231255
232- override def evalGetAndUpdate (f : A => F [A ]): F [A ] =
233- evalModify(a => f(a).map(aa => (aa, a)))
256+ private [effect] final class DefaultedAtomicCell [F [_], A ](
257+ atomicCell : AtomicCell [F , Option [A ]],
258+ default : A
259+ )(
260+ implicit F : Applicative [F ]
261+ ) extends CommonImpl [F , A ] {
262+ override def get : F [A ] =
263+ atomicCell.get.map(_.getOrElse(default))
234264
235- override def evalUpdateAndGet (f : A => F [A ]): F [A ] =
236- evalModify(a => f(a).map(aa => (aa, aa)))
265+ override def set (a : A ): F [Unit ] =
266+ if (a == default) atomicCell.set(None ) else atomicCell.set(Some (a))
267+
268+ override def evalModify [B ](f : A => F [(A , B )]): F [B ] =
269+ atomicCell.evalModify { opt =>
270+ val a = opt.getOrElse(default)
271+ f(a).map {
272+ case (result, b) =>
273+ if (result == default) (None , b) else (Some (result), b)
274+ }
275+ }
276+ }
277+
278+ implicit def atomicCellOptionSyntax [F [_], A ](
279+ atomicCell : AtomicCell [F , Option [A ]]
280+ )(
281+ implicit F : Applicative [F ]
282+ ): AtomicCellOptionOps [F , A ] =
283+ new AtomicCellOptionOps (atomicCell)
284+
285+ final class AtomicCellOptionOps [F [_], A ] private [effect] (
286+ atomicCell : AtomicCell [F , Option [A ]]
287+ )(
288+ implicit F : Applicative [F ]
289+ ) {
290+ def getOrElse (default : A ): F [A ] =
291+ atomicCell.get.map(_.getOrElse(default))
292+
293+ def unset : F [Unit ] =
294+ atomicCell.set(None )
295+
296+ def setValue (a : A ): F [Unit ] =
297+ atomicCell.set(Some (a))
298+
299+ def modifyValueIfSet [B ](f : A => (A , B )): F [Option [B ]] =
300+ evalModifyValueIfSet(a => F .pure(f(a)))
301+
302+ def evalModifyValueIfSet [B ](f : A => F [(A , B )]): F [Option [B ]] =
303+ atomicCell.evalModify {
304+ case None =>
305+ F .pure((None , None ))
306+
307+ case Some (a) =>
308+ f(a).map {
309+ case (result, b) =>
310+ (Some (result), Some (b))
311+ }
312+ }
313+
314+ def updateValueIfSet (f : A => A ): F [Unit ] =
315+ evalUpdateValueIfSet(a => F .pure(f(a)))
316+
317+ def evalUpdateValueIfSet (f : A => F [A ]): F [Unit ] =
318+ atomicCell.evalUpdate {
319+ case None => F .pure(None )
320+ case Some (a) => f(a).map(Some .apply)
321+ }
322+
323+ def getAndSetValue (a : A ): F [Option [A ]] =
324+ atomicCell.getAndSet(Some (a))
325+
326+ def withDefaultValue (default : A ): AtomicCell [F , A ] =
327+ defaultedAtomicCell(atomicCell, default)
237328 }
238329}
0 commit comments