{"action":"created","issue":{"url":"https://api.github.com/repos/scalaz/scalaz/issues/1450","repository_url":"https://api.github.com/repos/scalaz/scalaz","labels_url":"https://api.github.com/repos/scalaz/scalaz/issues/1450/labels{/name}","comments_url":"https://api.github.com/repos/scalaz/scalaz/issues/1450/comments","events_url":"https://api.github.com/repos/scalaz/scalaz/issues/1450/events","html_url":"https://github.com/scalaz/scalaz/pull/1450","id":254912418,"number":1450,"title":"IsCovariant, IsContravariant typeclasses. Reworked Maybe.","user":{"login":"TomasMikula","id":433439,"avatar_url":"https://avatars3.githubusercontent.com/u/433439?v=4","gravatar_id":"","url":"https://api.github.com/users/TomasMikula","html_url":"https://github.com/TomasMikula","followers_url":"https://api.github.com/users/TomasMikula/followers","following_url":"https://api.github.com/users/TomasMikula/following{/other_user}","gists_url":"https://api.github.com/users/TomasMikula/gists{/gist_id}","starred_url":"https://api.github.com/users/TomasMikula/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TomasMikula/subscriptions","organizations_url":"https://api.github.com/users/TomasMikula/orgs","repos_url":"https://api.github.com/users/TomasMikula/repos","events_url":"https://api.github.com/users/TomasMikula/events{/privacy}","received_events_url":"https://api.github.com/users/TomasMikula/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":5,"created_at":"2017-09-04T00:50:04Z","updated_at":"2017-09-04T17:25:48Z","closed_at":null,"author_association":"MEMBER","pull_request":{"url":"https://api.github.com/repos/scalaz/scalaz/pulls/1450","html_url":"https://github.com/scalaz/scalaz/pull/1450","diff_url":"https://github.com/scalaz/scalaz/pull/1450.diff","patch_url":"https://github.com/scalaz/scalaz/pull/1450.patch"},"body":"**`IsCovariant[F[_]]`** is a typeclass that witnesses that `F` is covariant, even though the covariant type parameter annotation has been forgotten:\r\n\r\n```scala\r\ntrait Module {\r\n  type Maybe[A]\r\n}\r\n\r\nobject ModuleImpl {\r\n  type Maybe[A] = Option[A]\r\n}\r\n\r\nval module: Module = ModuleImpl\r\n```\r\n\r\nHere, `module.Maybe` is declared invariant, however, it is in fact covariant. We can preserve the information about covariance via an instance of `IsCovariant[Maybe]`:\r\n\r\n```scala\r\ntrait Module {\r\n  type Maybe[A]\r\n\r\n  implicit def isCovariant: IsCovariant[Maybe]\r\n}\r\n```\r\n\r\n`IsCovariant[F]` allows to lift `Liskov[A, B]` into `Liskov[F[A], F[B]]`, and is a safer alternative to indiscriminately lifting `Liskov` for every `Functor`. In the case when the implementation of `F` is actually Scala-covariant, `IsCovariant[F]` instance can be defined without any unsafe casts. For cases when `F` really is invariant, one can still apply the reasoning described in [When can Liskov be lifted?](https://typelevel.org/blog/2014/03/09/liskov_lifting.html) to justify an unsafe cast, but on a case-by-case basis, rather than across the board for all `Functor`s, present and future.\r\n\r\n**`IsContravariant`** is analogous, for contravariant type constructors.\r\n\r\nI also **reworked `Maybe`** in this style. The extra benefit is that conversions to and from `Option` are identities.\r\n\r\nA side effect of making `Maybe` an abstract type is that it can't have a companion object, and thus any typeclass instances will be orphan, should anyone care.\r\n\r\nIf people approve of this I can implement `IList` in this style."},"comment":{"url":"https://api.github.com/repos/scalaz/scalaz/issues/comments/327005517","html_url":"https://github.com/scalaz/scalaz/pull/1450#issuecomment-327005517","issue_url":"https://api.github.com/repos/scalaz/scalaz/issues/1450","id":327005517,"user":{"login":"S11001001","id":219186,"avatar_url":"https://avatars3.githubusercontent.com/u/219186?v=4","gravatar_id":"","url":"https://api.github.com/users/S11001001","html_url":"https://github.com/S11001001","followers_url":"https://api.github.com/users/S11001001/followers","following_url":"https://api.github.com/users/S11001001/following{/other_user}","gists_url":"https://api.github.com/users/S11001001/gists{/gist_id}","starred_url":"https://api.github.com/users/S11001001/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/S11001001/subscriptions","organizations_url":"https://api.github.com/users/S11001001/orgs","repos_url":"https://api.github.com/users/S11001001/repos","events_url":"https://api.github.com/users/S11001001/events{/privacy}","received_events_url":"https://api.github.com/users/S11001001/received_events","type":"User","site_admin":false},"created_at":"2017-09-04T17:25:48Z","updated_at":"2017-09-04T17:25:48Z","author_association":"MEMBER","body":"> I don't know either. But if MaybeImpl was the only implementation of (sealed) MaybeModule, then the compiler would have all the information necessary to eliminate that invokevirtual.\r\n\r\nIt's probably irrelevant whether there's one implementation, and it's probably also irrelevant whether it's sealed. It's probably relevant whether the `val`'s concrete implementation is statically apparent at the point of call, and whether that `val` has a statically apparent implementation of `option` et al, &c.\r\n\r\n[Scala.js does an awesome job in that case.](https://failex.blogspot.com/2017/04/and-glorious-subst-to-come.html#markdown-header-would-you-box-a-javascript-string) In response to @Blaisorblade, I [tried with Scala JVM earlier, which did a terrible job and didn't inline anything](https://failex.blogspot.com/2017/04/the-high-cost-of-anyval-subclasses.html?showComment=1501441994974#c8343570638013278411)."}}
{"actor":{"display_login":"S11001001"}}