Lawless typeclasses 

Scalaz 7.0 contains several typeclasses that are now deemed lawless by Scalaz project: Length, Index, and Each. Some discussions can be found in #278 What to do about lawless classes? and (presumably) Bug in IndexedSeq Index typeclass. The three will be deprecated in 7.1, and removed in 7.2.

Length 

There’s a typeclass that expresses length. Here’s the typeclass contract of Length:

trait Length[F[_]]  { self =>
  def length[A](fa: F[A]): Int
}

This introduces length method. In Scala standard library it’s introduced by SeqLike, so it could become useful if there were data structure that does not extend SeqLike that has length.

Index 

For random access into a container, there’s Index:

trait Index[F[_]]  { self =>
  def index[A](fa: F[A], i: Int): Option[A]
}

This introduces index and indexOr methods:

trait IndexOps[F[_],A] extends Ops[F[A]] {
  final def index(n: Int): Option[A] = F.index(self, n)
  final def indexOr(default: => A, n: Int): A = F.indexOr(self, default, n)
}

This is similar to List(n) except it returns None for an out-of-range index:

scala> List(1, 2, 3)(3)
java.lang.IndexOutOfBoundsException: 3
        ...

scala> List(1, 2, 3) index 3
res62: Option[Int] = None

Each 

For running side effects along a data structure, there’s Each:

trait Each[F[_]]  { self =>
  def each[A](fa: F[A])(f: A => Unit)
}

This introduces foreach method:

sealed abstract class EachOps[F[_],A] extends Ops[F[A]] {
  final def foreach(f: A => Unit): Unit = F.each(self)(f)
}

Foldable or rolling your own? 

Some of the functionality above can be emulated using Foldable, but as @nuttycom suggested, that would force O(n) time even when the underlying data structure implements constant time for length and index. At that point, we’d be better off rolling our own Length if it’s actually useful to abstract over length.

If inconsistent implementations of these typeclasses were somehow compromising the typesafety I’d understand removing them from the library, but Length and Index sound like a legitimate abstraction of randomly accessible containers like Vector.

Pointed and Copointed 

There actually was another set of typeclasses that was axed earlier: Pointed and Copointed. There were more interesting arguments on them that can be found in Pointed/Copointed and Why not Pointed?:

Pointed has no useful laws and almost all applications people point to for it are actually abuses of ad hoc relationships it happens to have for the instances it does offer.

This actually is an interesting line of argument that I can understand. In other words, if any container can qualify as Pointed, the code using it either is not very useful or it’s likely making specific assumption about the instance.

Tweets to the editor