{"action":"created","issue":{"url":"https://api.github.com/repos/non/cats/issues/307","labels_url":"https://api.github.com/repos/non/cats/issues/307/labels{/name}","comments_url":"https://api.github.com/repos/non/cats/issues/307/comments","events_url":"https://api.github.com/repos/non/cats/issues/307/events","html_url":"https://github.com/non/cats/issues/307","id":73695675,"number":307,"title":"Consider an `Empty[+A]` type class","user":{"login":"non","id":218045,"avatar_url":"https://avatars.githubusercontent.com/u/218045?v=3","gravatar_id":"","url":"https://api.github.com/users/non","html_url":"https://github.com/non","followers_url":"https://api.github.com/users/non/followers","following_url":"https://api.github.com/users/non/following{/other_user}","gists_url":"https://api.github.com/users/non/gists{/gist_id}","starred_url":"https://api.github.com/users/non/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/non/subscriptions","organizations_url":"https://api.github.com/users/non/orgs","repos_url":"https://api.github.com/users/non/repos","events_url":"https://api.github.com/users/non/events{/privacy}","received_events_url":"https://api.github.com/users/non/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/non/cats/labels/enhancement","name":"enhancement","color":"84b6eb"},{"url":"https://api.github.com/repos/non/cats/labels/question","name":"question","color":"cc317c"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":5,"created_at":"2015-05-06T18:15:31Z","updated_at":"2015-06-22T16:44:59Z","closed_at":null,"body":"On Gitter @paulp brings up the possibility of a type class to represent emptiness. Here are some code snippets he posted:\r\n\r\n```scala\r\ntrait Empty[+A] { def emptyValue: A }\r\n\r\nobject Empty {\r\n  def apply[A](x: A): Empty[A]                        = new Empty[A] { def emptyValue = x }\r\n  def unapply[A](x: A)(implicit z: Empty[A]): Boolean = x == z.emptyValue\r\n}\r\n\r\ntrait EmptyInstances {\r\n  implicit val emptyAttrs: Empty[Attrs]                = Empty(Attrs.empty)\r\n  implicit val emptyOption: Empty[Option[Nothing]]     = Empty(None)\r\n  implicit val emptyjPath: Empty[jPath]                = Empty(jPath(\"\"))\r\n  implicit val emptySfsPath: Empty[SfsPath]            = Empty(SfsPath(\"\"))\r\n  implicit val emptyUid: Empty[Uid]                    = Empty(Uid(-1))\r\n  implicit val emptyName: Empty[Name]                  = Empty(Name(\"\"))\r\n  implicit val emptyNames: Empty[Names]                = Empty(Names(Vector()))\r\n  implicit val emptySize: Empty[Size.UnitsOf[Nothing]] = Empty(Size.empty)\r\n  implicit val emptyBytes: Empty[Bytes]                = Empty(Bytes(Array.emptyByteArray))\r\n  implicit val emptyJavaFileKey: Empty[JavaFileKey]    = Empty(JavaFileKey(empty, new Object))\r\n\r\n  val emptyNothing: Empty[Nothing] = new Empty[Nothing] { def emptyValue = abort(\"Empty[Nothing]\") }\r\n}\r\n\r\nimplicit class HasEmptyOps[A: Empty](x: A) {\r\n    def isEmpty: Boolean                     = x == empty[A]\r\n    def toOption: Option[A]                  = if (isEmpty) None else Some(x)\r\n    def | (alt: => A): A                     = if (isEmpty) alt else x\r\n    def map[B: Empty](f: A => B): B          = if (isEmpty) empty[B] else f(x)\r\n    def filter(p: Pred[A]): A                = if (!isEmpty && p(x)) x else empty[A]\r\n    def fold[B](none: => B)(some: A => B): B = if (isEmpty) none else some(x)\r\n  }\r\n```\r\n\r\nMy sense is that like Scalaz's earlier `Zero[A]` type class, many folks intuitively feel like something like this should exist.\r\n\r\nSome possible problems I can foresee:\r\n\r\n* Can we write laws about `Empty[A]` in isolation? If not, what laws can we write relative to other type classes?\r\n* Should it act on types like `A` or types like `F[_]`? The example here uses variance to support `Option[_]` via `Option[Nothing]` but there are many cases where that won't work.\r\n* How does `Empty[A]` relate to `Monoid[A]` if at all? `MonoidK[F[_]]`? `MonadFilter[F[_]]`?\r\n* What instances should we provide? Are there instances we should forbid?\r\n* If there is a distinction between `Monoid[A].empty` and `Empty[A].empty` will that be confusing?\r\n\r\nOne argument against including this in Cats is that a capability like *emptiness* might make more sense in some kind of collections framework, or some context that was a bit more concrete.\r\n\r\nAnyway, what do you all think?"},"comment":{"url":"https://api.github.com/repos/non/cats/issues/comments/114175092","html_url":"https://github.com/non/cats/issues/307#issuecomment-114175092","issue_url":"https://api.github.com/repos/non/cats/issues/307","id":114175092,"user":{"login":"paulp","id":9135,"avatar_url":"https://avatars.githubusercontent.com/u/9135?v=3","gravatar_id":"","url":"https://api.github.com/users/paulp","html_url":"https://github.com/paulp","followers_url":"https://api.github.com/users/paulp/followers","following_url":"https://api.github.com/users/paulp/following{/other_user}","gists_url":"https://api.github.com/users/paulp/gists{/gist_id}","starred_url":"https://api.github.com/users/paulp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/paulp/subscriptions","organizations_url":"https://api.github.com/users/paulp/orgs","repos_url":"https://api.github.com/users/paulp/repos","events_url":"https://api.github.com/users/paulp/events{/privacy}","received_events_url":"https://api.github.com/users/paulp/received_events","type":"User","site_admin":false},"created_at":"2015-06-22T16:44:59Z","updated_at":"2015-06-22T16:44:59Z","body":"> I could also imagine a getOrElseEmpty method attached to Option[A] that is implemented as getOrElse(Empty[A].emptyValue).\r\n\r\nRight, no need to imagine.\r\n\r\n```scala\r\nfinal class OptionOps[A](val x: Option[A]) extends AnyVal {\r\n  def | (alt: => A): A                             = x getOrElse alt\r\n  def ||(alt: => A): Option[A]                     = x orElse Some(alt)\r\n  def |?[A1 >: A](alt: => A1): A1                  = x getOrElse alt\r\n  def ||?[A1 >: A](alt: => Option[A1]): Option[A1] = x orElse alt\r\n\r\n  def zfold[B: Empty](f: A => B): B    = x.fold[B](emptyValue)(f)\r\n  def orEmpty(implicit z: Empty[A]): A = x getOrElse emptyValue[A]\r\n  def or(alt: => A): A                 = x getOrElse alt\r\n  def orFail(msg: String): A           = x getOrElse abort(msg)\r\n  def toDirect: Direct[A]              = Direct fromScala x.toVector\r\n}\r\n```"}}