zio / zio-prelude Goto Github PK
View Code? Open in Web Editor NEWA lightweight, distinctly Scala take on functional abstractions, with tight ZIO integration
Home Page: https://zio.dev/zio-prelude
License: Apache License 2.0
A lightweight, distinctly Scala take on functional abstractions, with tight ZIO integration
Home Page: https://zio.dev/zio-prelude
License: Apache License 2.0
ZIO[*, E, A]
ZManaged[*, E, A]
ZStream[*, E, A]
Schedule[*, A, B]
String
Int
Double
Float
Long
Byte
Char
Either
Option
List
Vector
Map
Tuple2
- Tuple22
It looks like the following will work:
Equal[A] with Ord[A]
given Equal[A]
and Ord[A]
)/cc @adamgfraser
How to reproduce:
Start sbt console
in ZIO Prelude project directory from the shell.
Try to e.g. instantiate a Sum
type using zio.prelude.Sum(1)
.
Output:
$ sbt console
[info] Loading global plugins from /Users/manfred/.sbt/1.0/plugins
[info] Loading settings for project zio-prelude-build from plugins.sbt ...
[info] Loading project definition from /Users/manfred/src/github/zio-prelude/project
[warn] There may be incompatibilities among your library dependencies; run 'evicted' to see detailed eviction warnings.
[info] Compiling 1 Scala source to /Users/manfred/src/github/zio-prelude/project/target/scala-2.12/sbt-1.0/classes ...
[info] Loading settings for project root from build.sbt ...
[info] Set current project to zio-prelude (in build file:/Users/manfred/src/github/zio-prelude/)
[info] Compiling 19 Scala sources to /Users/manfred/src/github/zio-prelude/target/scala-2.12/classes ...
[info] Starting scala interpreter...
Welcome to Scala 2.12.10 (Java HotSpot(TM) 64-Bit Server VM, Java 11.0.3).
Type in expressions for evaluation. Or try :help.
scala> zio.prelude.Sum(1)
<console>:5: error: illegal cyclic inheritance involving package object prelude
lazy val $result = res0
^
<console>:9: error: illegal cyclic inheritance involving package object prelude
"" + "\u001B[1m\u001B[34mres0\u001B[0m: \u001B[1m\u001B[32mzio.prelude.Sum.Type[Int]\u001B[0m = " + _root_.scala.runtime.ScalaRunTime.replStringOf(res0, 1000)
I tried using the latest version of sbt (1.3.7 -> 1.3.8), but this did not help.
Note that String
values must be printed in an escaped way, surrounded by double quotes, such that they can be copy/pasted into the REPL and reconstruct such strings identically.
AssociativeF.Both
=> AssociativeBothF
AssociativeF.Either
=> AssociativeEitherF
CommutativeF.Both
=> CommutativeBothF
CommutativeF.Either
=> CommutativeEitherF
IdentityF.Both
=> IdentityBothF
IdentityF.Either
=> IdentityEitherF
During my work on #24, I removed Identity instances for double and float. The reason I did this is because both of them violate identity laws. However, having in mind how useful these 2 types are, it makes sense to provide instances for them, but don't include them in law checking and document the reason for that.
We can upgrade to a snapshot version of ZIO and use the new laws functionality. This will both make it nicer for us to test them and allow us to identity improvement opportunities since I expect we will be one the heaviest users of this functionality. I am working on this.
String
Int
Double
Float
Long
Byte
Char
Either
Option
List
Vector
Map
Tuple2
- Tuple22
NonEmptyList[A]
should have methods that make sense only on non-empty lists: for example, safe head
, safe reduce
, etc.
I am working on this.
Closure
Associative
Commutative
Identity
???
List
Vector
Function[A, *]
- Function22[A, B, C, ..., *]
Map[K, *]
Either[A, *]
Tuple2[A, *]
- Tuple22[A, B, C, ..., *]
Try
Future
I am working on this.
zio.prelude.Id
ZManaged[*, E, A]
ZStream[*, E, A]
Schedule[*, A, B]
Fiber[E, *]
The microsite should probably have:
Currently the default renderer for Debug
renders the fully qualified name of each constructor. So for example a Validation
instance would be rendered as zio.prelude.Validation.Failure(zio.Chunk("some string"))
whereas the rendering given by toString
would be Failure(Chunk("some string"))
. While valid and perhaps optimal for compiling the rendering back to Scala, this is not the best default as it can lead to cluttered output when the constructor is deeply nested and inconsistent behavior depending on whether the user calls debug.render
and toString
even for user created types.
We should change the default renderer to render to unqualified name and add an additional renderer to render the fully qualified name. We can then test that the default rendering is equivalent to toString
for all standard types.
String
Int
Double
Float
Long
Byte
Char
Either
Option
List
Vector
Map
Tuple2
- Tuple22
We need to build out the laws functionality in ZIO Test to support higher kinded types like Covariant
. I am working on this.
For example:
def apply[A](a: A, as: A*): NonEmptyList[A] = ???
def fromCons[A](as: ::[A]): NonEmptyList[A] = ???
def fromIterable[A](a: A, as: Iterable[A]): NonEmptyList[A] = ???
def unfold[Z, A](start: Z)(project: Z => A)(iterate: Z => Option[Z]): NonEmptyList[A] = ???
...
Right now have some consistency in the rendering of algebraic data types. For example:
// Either
Repr.VConstructor(List("scala"), "Left", List(e.debug))
//
Debug.Repr.VConstructor(List("zio", "prelude"), "Validation.Failure", List(es.debug))
The Either
subtype doesn't even have "Either" anywhere (possibly because it is exported in the scala
package object), whereas the Validation
subtype not only has the parent type name but included it as part of the name of the type itself so it will always be rendered, which is inconsistent with the normal treatment of case classes with toString
.
I can see three potential options:
toString
, which seems desirable but could be debated.Failure
subtype. It would be inconsistent with toString
, but maybe that is okay. It could result in crowded output if types are subtypes of a large nested ADT hierarchy. To some extent it throws away information because we're just putting all this in the class name so the renderer doesn't have much ability to make decisions based on it.VConstructor
, maybe it becomes:final case class VConstructor(namespace: List[SString], parents: List[SString], name: SString, reprs: List[Repr])
This probably creates some ambiguity of what should be part of namespace
versus parents
(maybe a guideline is parents
are things that are super types of the type being rendered) but probably gives the renderer the most flexibility. That then still leaves the question of whether the default renderer should render the parents or not or possibly only the first parent.
We're starting to add instances for more data types that don't have well defined Equal
instances. For example, Ord
isn't a data type so we can't just look at two Ord
values and say whether they are equal. At the same time, we would like to be able to test laws for them to make sure our instances are well behaved.
One approach to do this would be to allow defining some "test" Equal
instances. For example, given two Ord[A]
values we could say they are equal "as far as we can tell" if they return the same ordering for a random sample of values. We could not know for certain the values are actually equal unless the domain was very small, but this could still probably provide a high degree of confidence in the correctness of instances.
This would probably require performing effects in determining the "to our best knowledge" equality, especially for effect types but would require defining Equal
instances that are not "true equality" in some sense, at least within our own internal test suites.
Does this make sense or is there another way we should approach this?
Forall
/ Exists
? ForAll
/ ForAny
?
And
/ Or
?
/cc @sken77
Currently, we aren't using any CI, which is fine while in prototyping phase, but the codebase grows quite quickly, and "pushes" the language more and more :).
CI configuration present in .config
is compatible with ZIO's CircleCI setup, and it will become quite useful once repository is transferred. Until that happens, I propose writing a minimal configuration that would run on CircleCI's free tier and perform only tests and format checks.
sealed trait NewtypeSmart[A] {
type Type
def make(value: A): Validation[String, Type] = wrap(value)
def unapply(value: Type): Some[A] = Some(unwrap(value))
def wrap(value: A): Validation[String, Type] = wrapAll[Id](value)
def unwrap(value: Type): A = unwrapAll[Id](value)
def wrapAll[F[_]](value: F[A]): Validation[String, F[Type]]
def unwrapAll[F[_]](value: F[Type]): F[A]
}
Etc.
Smart constructor newtypes / subtypes could utilize ZIO Test Assertion
, e.g.:
object Natural extends SubtypeSmart[Int](v => v.isGreaterThan(0) || v.equalTo(0))
type Natural = Natural.Type
This way failures have descriptive error messages without additional coding. Although possibly we need a mechanism to override these error messages in the event they are going to be shown to a user.
For some type class methods, in order to compute an answer, the parameters are both needed.
Good examples are:
But for other type class methods, there exist some instances that would not need to evaluate both parameters.
Good examples are:
These methods can benefit from by-name parameters, to reduce the amount of computation.
As mentioned in pull request #53 Adam suggested that I put the following Commutative
implementation for Either up for discussion here:
implicit def EitherCommutative[E: Commutative, A: Commutative]: Commutative[Either[E, A]] =
new Commutative[Either[E, A]] {
def combine(l: Either[E, A], r: Either[E, A]): Either[E, A] =
(l, r) match {
case (Right(l), Right(r)) => Right(l <> r)
case (Left(l), Right(_)) => Left(l)
case (Right(_), Left(r)) => Left(r)
case (Left(l), Left(r)) => Left(l <> r)
}
}
The implementation short-circuits in case one operand is Left, else it combines using Commutative
from either E or A.
String
Int
Double
Float
Long
Byte
Char
Either
Option
List
Vector
Map
Tuple2
- Tuple22
Given how many newtypes may exist, they should live in their own package, e.g.:
import zio.prelude.newtypes._
Set
???
String
Int
Double
Float
Long
Byte
Char
Either
Option
List
Vector
Map
Tuple2
- Tuple22
Right now, only Equal
instances created with make
will utilize refEq
. But increasingly, it's more common to create subtypes directly.
To ensure these are performant, we should make the ref check mandatory, which we can do by making the equal
method final and requiring subtypes implement some other method.
For example:
trait Equal[-A] { self =>
final def equal(l: A, r: A): Boolean = refEq(l, r) || checkEqual(l, r)
protected def checkEqual(l: A, r: A): Boolean
}
In this way, there is no question that the method will have an efficient implementation.
Mostly this can just use Hash.default
, except for weird cases like Array
.
Function2[*, A]
- Function[*, B, C, ...]
Seeing a couple of issues with the implicit instances for Nothing
. First, instances of parameterized on Nothing
such as Equal[Nothing
aren't available in implicit scope. Even if I move everything other than Nothing
to a lower priority scope I still get this error. I think there is some kind of logic that Nothing
does not qualify as a more specific type.
trait Equal[-A]
object Equal {
implicit val IntEqual: Equal[Int] =
new Equal[Int] {}
implicit val StringEqual: Equal[String] =
new Equal[String] {}
implicit val NothingEqual: Equal[Nothing] =
new Equal[Nothing] {}
}
// diverging implicits
implicitly[Equal[Nothing]]
Also, even if I define an Equal[Nothing]
instance locally to make it implicitly available derived instances aren't available unless defining them extremely explicitly:
trait Equal[-A]
object Equal {
implicit def EitherEqual[A: Equal, B: Equal]: Equal[Either[A, B]] =
new Equal[Either[A, B]] {}
implicit val IntEqual: Equal[Int] =
new Equal[Int] {}
}
implicit val NothingEqual: Equal[Nothing] =
new Equal[Nothing] {}
implicitly[Equal[Nothing]]
implicitly[Equal[Either[Int, Nothing]]] // Does not compile
Equal.EitherEqual[Int, Nothing]
NonEmptyList[A]
should extend Seq[A]
and should override all relevant methods to return a NonEmptyList[A]
when possible.
String
Int
Double
Float
Long
Byte
Char
Either
Option
List
Vector
Map
Tuple2
- Tuple22
Currently, every Identity
implies Associative
. But there is no part of Identity
's laws that require Associative
, so the supertype can be changed to Closure
to permit more instances.
Using, perhaps, NewtypeF
/ SubtypeF
, we should be able to provide lightweight newtyping / subtyping over polymorphic data types like First[A]
, Last[A]
, Min[A]
, Max[A]
.
Right now our laws are a mixture of booleans and TestResult
. Use of booleans, such as a > b
, will result in quite poor error messages. Ideally, every law failure produces a richly detailed error message, fully utilizing ZIO Test's facilities for reporting.
We fake this right now using <->
as an alternate operator, but this doesn't scale: what would we choose for >
, >=
, etc.
I think it might be useful to define a new type, like Arbitrary
/ Arb
:
object Arb extends SubtypeF
type Arb[A] = Arb.Type[A]
implicit class ArbSyntax[A](value: Arb[A]) {
def === (that: Arb[A])(implicit equal: Equal[A]): TestResult = ???
def > (that: Arb[A])(implicit ord: Ord[A]): TestResult = ???
...
}
Then to define (redefine?) Law1
, etc. classes such that they are passed Arb[A]
instead of A
.
Then laws can have this form:
(a1 < a2) && (a2 < a3) ==> (a1 < a3)
(a1 === a2) ==> (a2 === a1)
But they will generate detailed error messages.
/cc @adamgfraser
The Scaladoc should explain the purpose, the features, and have examples.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.