Skip to content
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

Add Leibniz equality #1177

Closed
tel opened this issue Jul 4, 2016 · 7 comments
Closed

Add Leibniz equality #1177

tel opened this issue Jul 4, 2016 · 7 comments

Comments

@tel
Copy link
Contributor

tel commented Jul 4, 2016

This might fall a bit outside the standard "only the things you need" toolkit, but having a notion of Leibniz equality makes handling type equalities much easier and is useful for some pure functional idioms. Scalaz's implementation is more powerful to account for subtyping, but a simpler version might be the following

I also propose calling it something less scary than "Leibniz equality", so Teq seems reasonable.

import scala.language.higherKinds
import scala.language.implicitConversions

/**
  * `Teq[A, B]` is a witness that the types `A` and `B` are equal. It is more
  * powerful than the standard `A =:= B` since it offers the ability to
  * convert types appearing within other structures.
  */
sealed abstract class Teq[A, B] {
  def subst[F[_]](fa: F[A]): F[B]

  final def apply(a: A): B =
    Teq.witness(this)(a)

  final def andThen[C](next: B Teq C): A Teq C =
    next.subst[A Teq ?](this)

  final def compose[C](prev: C Teq A): C Teq B =
    prev andThen this

  final def from: B Teq A =
    this.subst[? Teq A](Teq.refl)

  final def lift[F[_]]: F[A] Teq F[B] =
    subst[Lambda[X => F[A] Teq F[X]]](Teq.refl)
}

object Teq {

  /**
    * The only real value of `Teq` is the statement `A Teq A`.
    */
  implicit def refl[A]: A Teq A = new Teq[A, A] {
    def subst[F[_]](fa: F[A]): F[A] = fa
  }

  /**
    * A `Teq` immediately furnishes a coercion function.
    */
  implicit def witness[A, B](t: A Teq B): A => B =
    t.subst[A => ?](identity)

  /**
    * We can convert `A Teq B` to a `A =:= B` witness via substitution.
    */
  implicit def scalaEq[A, B](t: A Teq B): A =:= B =
    t.subst[A =:= ?](implicitly[A =:= A])

}
@tpolecat
Copy link
Member

tpolecat commented Jul 5, 2016

I think both Leibniz and Lizkov from scalaz end up being useful, although maybe more for library authors than for end users. In any case 👍 from me.

@tel
Copy link
Contributor Author

tel commented Jul 5, 2016

I'm a little curious about Lizkov and the subtyping parameters around Leibniz.

Also, any thoughts on TEq as the name? The other option I thought about was Is.

@adelbertc
Copy link
Contributor

I've found these to be useful, 👍

As for naming, I would prefer Leibniz and Liskov if only to provide access to the general Google-able principle

@tel
Copy link
Contributor Author

tel commented Jul 5, 2016

I suppose I'm not sure how that particular bikeshed is painted in Cats. A Is B reads nicely to me and I like the infix, but Leibniz is also fine in my opinion. Is it sufficient to just document the technical term?

@tel
Copy link
Contributor Author

tel commented Jul 5, 2016

I'll turn this into a PR to make the discussion a bit more concrete.

@tel
Copy link
Contributor Author

tel commented Jul 7, 2016

@non, to be clear, I think this is a valuable shed to paint in a project like this and I definitely defer to those familiar with the style and spirit of the project!

@peterneyens
Copy link
Collaborator

Added in #1178.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants