typelevel / algebra

Experimental project to lay out basic algebra type classes
https://typelevel.org/algebra/
Other
378 stars 69 forks source link

Symbolic infix operators #78

Open TomasMikula opened 9 years ago

TomasMikula commented 9 years ago

Hi,

is there a plan to add symbolic infix operators as in Cats?

non commented 9 years ago

Not currently. There is (or at least was) a difference in design philosophy between Spire (which encourages symbolic infix operators) and Algebird (which discourages them). I would be open to adding operators here if @johnynek and @avibryant want them.

TomasMikula commented 9 years ago

Since Cats already add symbolic operators for Algebra type classes, would distributing symbolic operators in a separate JAR be an acceptable compromise?

non commented 9 years ago

@TomasMikula Sure -- I would be happy to add an algebra-syntax module that provides this functionality. I think it's just a matter of waiting and seeing how the other stakeholders feel about the idea.

johnynek commented 9 years ago

We can revisit this, but the goals have drifted a bit on what Algebra is.

I think it is kind of a reference implementation of modeling algebra with scala. It could also be the base for algebird and spire (but it seems progress is slow there), and if we dream, we can imagine algebra being scala.algebra and making it into the standard library.

The counter argument to the above is that such coupling causes a lot of binary compatibility issues. You have some core library that everyone depends on and now you need to be REALLY sure you don't break binary compatibility almost ever. Alternatively, you have adapter implicit methods that can wrap a Spire Monoid in an Algebra Monoid, etc...

Finally, alebra seems focused (in my mind) on building a strong base for abstraction. Spire seems to be (mostly) about high performance mathematics with scala, Algebird is (mostly) about modeling sketch/approximation algorithms algebraically (so they can be plugged in generally to scalding/spark/summingbird).

The binary compatibility keeps me up at night (literally sometimes) because we deal with a giant monorepo at Twitter and we see these kinds of pains a lot.

johnynek commented 9 years ago

That said, +1 to algebra-syntax. No issues there.

TomasMikula commented 9 years ago

Thanks for the comment. I'm having some trouble understanding what you are implying, though.

I think it is kind of a reference implementation of modeling algebra with scala. It could also be the base for algebird and spire (but it seems progress is slow there), and if we dream, we can imagine algebra being scala.algebra and making it into the standard library.

The counter argument to the above is that such coupling causes a lot of binary compatibility issues. You have some core library that everyone depends on and now you need to be REALLY sure you don't break binary compatibility almost ever.

Are you saying that in order to avoid binary compatibility issues, one option for Algebra's future is to stay just an experimental project / reference implementation and not base production code on it? That certainly takes the burden off of Algebra, but then what good is Algebra if it's not supposed to be used in production?

Finally, alebra seems focused (in my mind) on building a strong base for abstraction. Spire seems to be (mostly) about high performance mathematics with scala, Algebird is (mostly) about modeling sketch/approximation algorithms algebraically (so they can be plugged in generally to scalding/spark/summingbird).

Is this to say that striving for the ideal of having both abstraction and high performance is futile?

non commented 9 years ago

@TomasMikula Here's my 2¢:

  1. algebra-core is currently used in Cats. So I am already committed to using it in production, and it is intended to be useful.
  2. Spire's current roadmap has one more release planned for bug fixes, and then there will be a PR migrating to Algebra's type classes (i.e. algebra-core). Whether or not Algebird moves over remains to be seen, but at least Cats and Spire will be using these type classes.
  3. Spire will not be using algebra-std instances, at least not initially. This is to preserve the existing behavior for Spire users. It would be too much effort to try to shoehorn every one of Spire's instances into Algebra, so for now we aren't going to try. Similarly Spire would probably continue using its own syntax, at least initially.
  4. When we started these conversations, the thing we wanted was a common interface for defining how libraries using algebraic type classes could inter-operate. I think algebra-core does a great job at this. We hadn't necessarily agreed on syncing up implementations.
  5. The reason algebra-std was written, originally, was as a reference implementation to help verify that our laws (i.e. algebra-laws) were useful. The Rat class there is definitely a toy, and not meant for production use. Please use spire.math.Rational (or another type) instead! :smile:
  6. I think @johnynek's point was to figure out what Algebra's role should be as far as standalone implementations go (i.e. the instances, the syntax, the concrete data types, the algorithms). These are the things that differentiate Algebird, Spire, Cats, and many other libraries who I could imagine might use Algebra to inter-operate while preserving their own unique flavor.

Does this makes sense? I think that in terms of libraries programming to an Algebra interface (algebra-core), it is 100% production-ready. As far as using Algebra in a stand-alone way (without rolling your own syntax, instances, algorithms, etc) it is still immature (and making it mature may not be our primary goal).

johnynek commented 9 years ago

just to be clear, I'm not saying algebra should not be used in production, what I am saying is that we really haven't solved the diamond dependency problem on the JVM, and it is particularly acute with core libraries. So, imagine a giant code base (10s of millions of lines), and you add a new core dependency to several projects (that previously had no common dependencies), now all of a sudden, you have made your satisfiability problem much harder since all downstream of this core have to be upgraded in lock-step.

All of that is not that relevant to people building smaller apps, or those outside of monorepos where their entire system does not have to be on the same version of code (PS: I'm not a huge monorepo fan for this issue of it wanting to swallow the world).

So, the anxiety situation I have is this: someone wants to use @travisbrown finagle + cats stuff. And it's great. And then, algebird depends on algebra. Now, at Twitter, we have to upgrade algebird + (finagle + cats) at the same time (in the same PR) if algebra has any binary incompatibilities. This problem becomes more and more compounded as algebra and cats get adopted.

This is kind of the same reason we are still on scala 2.10 (though big progress to moving towards 2.11), because we have to switch all of twitter at one go. The more core libraries we have with many dependencies, the more we see this problem.

All of the above is just anxiety. If I actually did a bit more work on this project, I would have already implemented the MIMA check, and we could just be SUPER conservative about ever breaking binary compatibility on algebra-core. Then we'd probably be okay, and algebird would feel safe basing off algebra. But at this point, algebird is kind of mission critical at Twitter, and we have to be careful about taking dependencies there.

TomasMikula commented 9 years ago

Thank you @non and @johnynek, makes sense. For the record, I'm mostly interested in Algebra's type classes and laws, less so in the instances. (Btw, for law checking, I'm not very happy with Discipline, but that's for another conversation.)

non commented 9 years ago

@johnynek I am already over-subscribed, but I will totally try to get MiMA working for algebra. To some extent I think we can hopefully just freeze algebra-core, and as long as that is the only dependency, things should be fine. But I hear you about dependency pain.

TomasMikula commented 9 years ago

Regarding freezing algebra-core, is there a chance to revisit the return type of Order#compare and PartialOrder#partialCompare being Int and Double, respectively. I assume this has to do with optimizing the Order[Int] instance so that compare(a, b) reduces to a simple a-b, but otherwise seems like a slap in the face of type safety.

non commented 9 years ago

Ironically it's not safe to use a - b with signed values, because 0 - Int.MinValue would be Int.MinValue, which has the wrong sign.

The rationale in Spire is that we want generic code to operate as efficiently as direct code, and the return type for the various compare methods in Java/Scala is Int (except for Float and Double, which are actually partial orderings anyway). It would be easy to create parallel methods that use compare and partialCompare but return some kind of ADT, but I would be very reluctant to refactor Order and PartialOrder in terms of those methods.

TomasMikula commented 9 years ago

What about a value class wrapping the Int and/or objects (LT, EQ, GT) for pattern matching?

case class Cmp(raw: Int) extends AnyVal {
  def isLt  = raw <  0
  def isLte = raw <= 0
  def isEq  = raw == 0
  def isGte = raw >= 0
  def isGt  = raw >  0
}

object Cmp {

  object LT {
    def apply = Cmp(-1)
    def unapply(x: Cmp): Boolean = x.isLt
  }

  object EQ {
    def apply = Cmp(0)
    def unapply(x: Cmp): Boolean = x.isEq
  }

  object GT {
    def apply = Cmp(1)
    def unapply(x: Cmp): Boolean = x.isGt
  }

  implicit def cmpEq: Eq[Cmp] = new Eq[Cmp] {
    def eqv(x: Cmp, y: Cmp): Boolean = (x, y) match {
      case (LT(), LT()) => true
      case (EQ(), EQ()) => true
      case (GT(), GT()) => true
      case _            => false
    }
  }

}