Open kubukoz opened 3 years ago
I'm definitely not a fan of this. The problem is that this is a false abstraction: fiber locals in other IO
s behave entirely differently from CE3's, and from each other. Users can't meaningfully write code which is generic over all of them. I would rather punt the problem downstream a bit and allow users to create a la carte abstractions (similar to UnsafeRun
in weaver and in cats-effect-testing) which meet their needs directly when they want to abstract over the different implementations.
@djspiewak Not sure if you caught it in the PR, but I ended up parameterizing FiberLocal
over F
and added a specialized constructor for IO
in the companion object. That way we're not committing to creating a type class for it today, but if it becomes feasible in the future then we could without much breakage. Though I think we would need to move it into std
for that to work. How do you feel about that?
Uh. Hmm. I'll need to think about it a bit. I really don't think it's going to be something we want to abstract over, but I see what you're saying about binary compatibility.
My original thinking was that our definition of FiberLocal
is so simple that Monix and ZIO can implement it with a subset of their functionality i.e. all they need is copy-on-fork semantics, which I think they have. Need to double check. FiberRef
goes further and allows shared scopes, but in a completely compositional manner that doesn't demand any extra runtime behavior (via Ref
)
Monix and ZIO can implement it with a subset of their functionality i.e. all they need is copy-on-fork semantics, which I think they have
We definitely don't have copy-on-fork in monix. We opted for making user isolate region explicitly to have CE2 laws like fa.start.flatMap(_.join) <-> fa
when fa
is mutating local context
Related: #1847
Is Monix support still a blocker for having an official FiberLocal
? Monix maintenance has slowed (last release is 2 years ago), and there isn't even CE3 support in any of the series/*
branches. If the other effect types in the ecosystem support copy-on-fork, I don't really see a blocker here?
@zarthross
If the other effect types in the ecosystem support copy-on-fork, I don't really see a blocker here?
I think it's less a question of what implementations do exist and more a question of what implementations could exist. The blocker is that there isn't an argument why one particular semantic should be preferred for an abstraction.
Taking a step-back, what is your use-case? Is it possible that the Local
abstraction from Cats MTL could address your needs? See also https://github.com/typelevel/cats-effect/issues/3385.
I do think Cats MTL Local
for IOLocal
is a good idea and I will likely use it, part of my issue here is when I create the instance of Local
. Most of the code I'm dealing with is still in an abstract F[_]
and so creating an instance of IOLocal
where I want it is less than Ideal. What I'm effectively looking for is a GenFiberLocal
.
I suppose I could hack one together up and create one for Local
that could be passed down to where i need it.
trait GenMTLLocal[F[_]]{
def local[A](default: A): F[Local[F, A]]
}
object GenMTLLocal {
implicit def ioLocal: new GenMTLLocal[IO] {
def local[A](default: A): IO[Local[IO, A]] = IOLocal(default).map(oxidized.instances.catsMtlEffectLocalForIO)
}
}
I think it's less a question of what implementations do exist and more a question of what implementations could exist. The blocker is that there isn't an argument why one particular semantic should be preferred for an abstraction.
Not every effect monad implements both Sync
and Async
, but we still have those. Not every effect system would have to have a FiberLocal
instance for FiberLocal
to be useful. We can have effect types that don't fully implement the cats-effect typeclass eco system and those still be useful within it.
Not every effect monad implements both
Sync
andAsync
, but we still have those.
I'm sorry, I think we talked past each other. My point was that we don't create abstractions by selecting the semantics of an existing implementation and simply swapping in an F[_]
. Ideally, we thoughtfully design abstractions independently of implementations, giving consideration to laws and orthogonality.
To frame this issue another way: what is the (theoretical) reason that we should choose the copy-on-fork semantic for this abstraction, independent of what implementations are doing?
Follow-up to #1393.
I think users will likely want to create fiber locals for an abstract effect. We can surely create them in IO and other effects that have this functionality, it is probably possible for monad transformers too (I might be missing something).
It would be great to have an abstraction for this that people can depend on instead of sticking to IO: