@@ -153,35 +153,29 @@ object AtomicCell {
153
153
of(M .empty)(F )
154
154
}
155
155
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))
162
162
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
166
165
)(
167
166
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
+ }
173
171
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 ] {
174
176
override def modify [B ](f : A => (A , B )): F [B ] =
175
177
evalModify(a => F .pure(f(a)))
176
178
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
-
185
179
override def evalUpdate (f : A => F [A ]): F [Unit ] =
186
180
evalModify(a => f(a).map(aa => (aa, ())))
187
181
@@ -192,12 +186,33 @@ object AtomicCell {
192
186
evalModify(a => f(a).map(aa => (aa, aa)))
193
187
}
194
188
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 ](
196
211
init : A ,
197
- mutex : Mutex [ F ]
212
+ lock : Resource [ F , Unit ]
198
213
)(
199
214
implicit F : Async [F ]
200
- ) extends AtomicCell [F , A ] {
215
+ ) extends CommonImpl [F , A ] {
201
216
@ volatile private var cell : A = init
202
217
203
218
override def get : F [A ] =
@@ -206,17 +221,14 @@ object AtomicCell {
206
221
}
207
222
208
223
override def set (a : A ): F [Unit ] =
209
- mutex. lock.surround {
224
+ lock.surround {
210
225
F .delay {
211
226
cell = a
212
227
}
213
228
}
214
229
215
- override def modify [B ](f : A => (A , B )): F [B ] =
216
- evalModify(a => F .pure(f(a)))
217
-
218
230
override def evalModify [B ](f : A => F [(A , B )]): F [B ] =
219
- mutex. lock.surround {
231
+ lock.surround {
220
232
F .delay(cell).flatMap(f).flatMap {
221
233
case (a, b) =>
222
234
F .delay {
@@ -225,14 +237,93 @@ object AtomicCell {
225
237
}
226
238
}
227
239
}
240
+ }
228
241
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)
231
255
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))
234
264
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)
237
328
}
238
329
}
0 commit comments