-
Notifications
You must be signed in to change notification settings - Fork 530
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Incorporate MapRef into Std #2464
Conversation
* Also useful for anytime a shared storage location is used for a ref, i.e. DB or Redis to not waste | ||
* space. | ||
**/ | ||
def defaultedRef[F[_]: Functor, A: Eq](ref: Ref[F, Option[A]], default: A): Ref[F, A] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense here as this is where it necessary. But might be useful elsewhere. We could also rely on global equality if we like being risky or offer a global equality version.
@@ -169,4 +169,40 @@ class MapRefTrieSpec extends BaseSpec { | |||
|
|||
} | |||
|
|||
"MapRef" should { // Requires unsafeRunSync so doesn't work with JS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting note, it's not just that JS doesn't have unsafeRunSync
so this test doesn't work, it's actually that this test is meaningless on JS anyway because concurrent modifications are impossible :)
Just a thought, could we abstract the current |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like this as an addition to the stdlib. My concerns basically boil down to the following:
- Confusing and IMO unnecessary
of
/in
constructor duplication - Inaccurate typeclass constraints faked by using
unit.map(_ => ...)
rather thandelay(...)
- Syntax requires unnecessary imports
- Minor bikeshedding on test structure
- Lots of unnecessary and likely-inefficient use of
lazy val
rather thandef
Concurrent[F] | ||
.unit | ||
.map(_ => scala.collection.concurrent.TrieMap.empty[K, V]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very leery of something like this. :-)
class HandleRef(k: K) extends Ref[F, Option[V]] { | ||
|
||
def access: F[(Option[V], Option[V] => F[Boolean])] = for { | ||
hasBeenCalled <- Applicative[F].unit.map(_ => new AtomicBoolean(false)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…and here too. Why not simply embrace the fact that this is really forcing Sync[F]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to open an issue for what I'm about to say, and I also want to give a general review of MapRef (tl;dr mostly amazing, some api bikeshedding), but I'm going to drop it here in case I forget: we should relax the semantics on access
to not need hasBeenCalled
. It was in "broken" form in fs2 for ages, the only reason this was introduced was to conform to the docs, and it's a pain in the arse for things that want to implement Ref, like SignallingRef or MapRef.
* equally distributed Hash, the contention should allow for interaction like a general | ||
* datastructure. Created in G, operates in F. | ||
*/ | ||
def inShardedImmutableMap[G[_]: Sync, F[_]: Async, K, V]( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we more fully embrace Sync
, then we don't need to have the of
/in
split in the constructors.
import cats.syntax.all._ | ||
|
||
trait MapRefSyntax { | ||
implicit def mapRefOptionSyntax[F[_], K, V]( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can actually stick this into the MapRef
companion and it won't require the import.
|
||
"MapRef" should { | ||
|
||
"MapRef.ofSingleImmutableMapRef - concurrent modifications" in real { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uh… we're in Specs2, so this kind of test name is pretty confusing. :-) I would prefer if we did "ofSingleImmutableMapRef" >> { ... }
and then inside that block have "concurrent modifications" >> real {
etc etc.
This seems pretty useful. What I find very strange is that it uses universal equality/hash for the keys, while there is a |
Proposal:
Thoughts? Questions with this: Def we want configurable CHM initialCapacity, loadFactor and concurrencyLevel? For The Ref of Map Implementation should there be a default striping we leverage? |
I'm in favour of the proposal above, and I think it would be good to get this into CE :) |
I would recommend basing the default implementation on |
Fixes #2444
Integrates MapRef.
Features: ShardedMapRef, ConcurrentHashMap, and Scala Concurrent Map Implementations, and Defaulted Ref/MapRef.
Why Care? This enables keyed concurrent state machines that don't space leak. It basically allows almost any state machine with a default state to be lifted to a keyed version of the same state machine that is removed entirely when it enters some zero state.