Open rnd4222 opened 4 years ago
@rnd4222 Thanks for raising! This is definitely something we have thought about it since it is the approach that other functional programming libraries have taken. However, I don't think it is the right approach for us.
First, I don't think that it makes sense for ZIO to depend on ZIO Prelude. ZIO is a library that is focused on solving problems in asynchronous and concurrent programming using the power of pure functional programming. Some users of ZIO are obviously very familiar with functional programming abstractions but some users have no knowledge or interest in them. So making ZIO depend on ZIO Prelude would inevitably lead to these abstractions leaking into the ZIO API in a way that would go against ZIO's goals of zero dependencies and of using functional programming under the hood to solve concrete problems without users having to learn about things like type classes.
Second, I think it does make sense for ZIO Prelude to depend on ZIO. The extent to which ZIO Prelude depends on ZIO is limited to a few relatively specific areas: (1) providing instances for ZIO data types, (2) using ZIO Test to describe to laws as values, and (3) using ZIO Test assertions to support composable validation. If you look at a type class like Equal
or Traversable
ZIO appears nowhere in the definition of those abstractions. So I don't think that tight integration with ZIO results in a loss of abstraction and it provides concrete benefits.
More broadly, I think the goals of this library are different than you suggest. ZIO Prelude is focused on providing fundamental functional programming abstractions (e.g. associative ways of combining data, ways of traversing data structures) rather than ways of abstracting over effect types. There are no type classes describing concepts like time or resources that you would find in such an effort. There are other libraries like Izumi BIO that support abstracting over effect types in a modern way for users who are interested in that. So I don't think the other concerns you raise are applicable.
Hope this helps!
There is a third option - neither depends on the other, instances live in their own module.
That is certainly a possibility. I think the practical implications are:
apply
method would have to go away and be provided in this third library as some kind of implicit syntax or alternative object.I don't think any of these are the end of the world but I tend to on balance think they make life more difficult for users for a largely theoretical benefit.
The logical conclusion of that approach would be to extract assertions and validations to a mini library. After all there is no reason for them to depend on zio.
That is possible.
The assertions themselves actually do have a reason to depend on ZIO because they describe both effectual and pure assertions. Of course you could split that but then you are taking one data type that unifies them and somewhat artificially splitting it up.
I think Validation
is a pretty standard type that you expect in a library of functional programming abstractions and is one of the "easy wins" you get from using one. There is just a trade off here that at some point libraries get so small that it is less efficient for both users and maintainers to have them separate. Not having Validation
as part of ZIO Prelude feels that way to me but that is just my opinion.
@adamgfraser thanks for your response.
Does it makes sense to make a very basic package (say, zio-prelude-kernel
) which doesn't have any dependencies, and depend on it in a zio-prelude
?
Would like to bump this, as I've seen comments/concerns about prelude depending on ZIO core. In our case this is certainly a blocker for using in certain scenarios (we'd like to keep our core domain pure of any IO effects, but Validation and ZPure would be very useful)
What are the blockers for removing direct dependency on ZIO?
Personally I view this as a non-goal.
While certainly possible, this would have the following disadvantages:
ZIO
effects or STM
effects).Chunk
or NonEmptyChunk
are used internally (e..g in representing the potentially multiple errors in Validation
). While these could certainly be replaced by List
and ::
that would also go against our design goals of consistently using data types like Chunk
throughout.I think the answer if you don't want to use effects is just don't use effects. Use Validation
and don't use ZIO
. This could potentially be augmented by static analysis tools but I think artificially separating ZIO and ZIO Prelude is not the way to go.
I actually agree with most of your points, the alternative here is really to make all interop with ZIO via extension methods (.toZIO
and friends) which could work, but is not ideal. Also, inverting the dependency is also a non-solution, because it goes against the goals of ZIO.
I feel that that the "lesser evil" here is possibly extracting a third library, like @rnd4222 suggested above - make a "zio-prelude-kernel" which would contain the base typeclasses, newtypes and assertions, and make fx and friends (i.e. everything that depends on ZIO) into zio-prelude
proper. I think this way it can solve both issues of having a standalone prelude stdlib and extras for those who need it.
I think this amounts to the same issues discussed above. It depends a little bit on what the scope is, but to the extent that type classes definitions don't depend on ZIO then instances for ZIO data types become orphans. To the extent that data types like Validation
don't depend on ZIO then methods to interoperate with ZIO such as converting a Validation
to a ZIO
have to become extension methods and we can't use ZIO data types like Chunk
.
I think the right way to look at this is ZIO is a base library and just like the Scala standard library provides some data types like Future
or mutable collections that might not make sense in all parts of your code base. The right way to deal with that is just to not use those data types, not to say you don't want the dependency.
It is important that
zio
would depend onzio-prelude
, not the other way around, for the following reason:Loss of Abstraction. Any abstraction that features a concrete instance of that abstraction inside the abstraction (e.g.
ZIO
appears in the zio-prelude typeclasses) is no longer an abstraction at all. To define the meaning of an effect in terms of a concrete data type is to abandon the conception of abstraction. There is no longer abstraction, just layers of ceremony around pretend abstraction.Massive Wasted Effort. For example,
cats-effect
guys regularly port features back and forth between cats-effectIO
and monixTask
. The tiny functional programming ecosystem is maintaining essentially two copies of the same data type, with the caveat that monixTask
can be regarded as a much better version of cats-effectIO
, supporting everything that cats-effectIO
does, in a more flexible way, and with more features.Harmful Biases. There is non-zero possibility that some 3rd-party library would get cool feature that is not easy to incorporate into
ZIO
. Much of the pushback would came from the fact that any significant design decision must be accompanied by significant re-engineering of theZIO
data type, resulting in decisions that are biased toward the current design, even if it's suboptimal in the landscape of possible designs. In the best possible world,zio-prelude
provides a layer of abstraction which is influenced only by the libraries that consume and provide instances.Crippling Innovation. There is repeated significant demand to add more features to the
ZIO
datatype - features that may be present in other libraries like cats-effectIO
and monixTask
. Yet adding an endless battery of never-ending features is not good for this library, because it increases maintenance burden and spirals the project away from its initial goals. IfZIO
were a separate project, then maintainers of other projects could feel free to contribute improvements so long as they were committed to maintaining them; and likely they would diverge fromZIO
and evolve in their own ways.Zero Benefit. Having a dependency on
zio
inzio-prelude
has zero benefit and all cost. In a world in which there is no such dependency, and the user prefersZIO
for some reason, this user can switch their SBT build file fromzio-prelude
to justzio
and continue to use everything almost exactly as they do today. In fact, such a project can easily add extension methods so the user still hastoZIO
and other features they might like to use.In summary, because having a concrete data type in the hierarchy destroys abstraction, results in massive wasted effort, biases design discussions towards a "reference type", cripples innovation, and has zero benefit, removing dependency on
zio
makes a ton of sense.Then the original design goal of this project to provide a lightweight way to abstract over the different effect data types can be restored, in a healthier way that will lead to better abstractions and possibly even an improved
ZIO
.