Skip to content
This repository has been archived by the owner on Feb 27, 2021. It is now read-only.

Monad[Id] is ambiguous #20

Open
edmundnoble opened this issue May 25, 2018 · 5 comments
Open

Monad[Id] is ambiguous #20

edmundnoble opened this issue May 25, 2018 · 5 comments
Labels

Comments

@edmundnoble
Copy link
Contributor

edmundnoble commented May 25, 2018

To reproduce:

import scalaz._, Scalaz._, shims._
Monad[Id]

Result:

<console>:26: error: ambiguous implicit values:
 both method flatMapToScalaz in trait FlatMapConversions of type [F[_]](implicit FC: shims.util.Capture[cats.FlatMap[F]])scalaz.BindRec[F] with shims.conversions.Synthetic
 and value idInstance in package scalaz of type => scalaz.Traverse1[scalaz.Id.Id] with scalaz.Monad[scalaz.Id.Id] with scalaz.BindRec[scalaz.Id.Id] with scalaz.Comonad[scalaz.Id.Id] with scalaz.Distributive[scalaz.Id.Id] with scalaz.Zip[scalaz.Id.Id] with scalaz.Unzip[scalaz.Id.Id] with scalaz.Align[scalaz.Id.Id] with scalaz.Cozip[scalaz.Id.Id] with scalaz.Optional[scalaz.Id.Id]
 match expected type scalaz.BindRec[[X]X]
       val res0 =

First encountered on shims 1.1, upgraded to shims 1.2.1 where the problem persisted.
Scalaz version 7.2.17, no cats imports.

EDIT: Worked around by shadowing idInstance in the scope where it's used. Perhaps shims is providing the instance itself somehow?

@djspiewak
Copy link
Owner

Interesting. Shims isn't providing the instance itself, but it's hard to say for sure. Id is basically just a terrible type to be using in these sorts of cases because of aliasing issues in scalac, but it definitely shouldn't be broken by shims. I'll take a look.

@djspiewak djspiewak added the bug label May 27, 2018
@djspiewak
Copy link
Owner

Interesting that BindRec[Id] materializes just fine…

@djspiewak
Copy link
Owner

scala> import scalaz._, Scalaz._, shims._
import scalaz._, Scalaz._, shims._
<console>:11: warning: Unused import
       import scalaz._, Scalaz._, shims._
                               ^
<console>:11: warning: Unused import
       import scalaz._, Scalaz._, shims._
                                        ^
import scalaz._
import Scalaz._
import shims._

scala> Monad[Id]
Monad[Id]
<console>:20: error: ambiguous implicit values:
 both method flatMapToScalaz in trait FlatMapConversions of type [F[_]](implicit FC: shims.util.Capture[cats.FlatMap[F]])scalaz.BindRec[F] with shims.conversions.Synthetic
 and value idInstance in package scalaz of type => scalaz.Traverse1[scalaz.Id.Id] with scalaz.Monad[scalaz.Id.Id] with scalaz.BindRec[scalaz.Id.Id] with scalaz.Comonad[scalaz.Id.Id] with scalaz.Distributive[scalaz.Id.Id] with scalaz.Zip[scalaz.Id.Id] with scalaz.Unzip[scalaz.Id.Id] with scalaz.Align[scalaz.Id.Id] with scalaz.Cozip[scalaz.Id.Id] with scalaz.Optional[scalaz.Id.Id]
 match expected type scalaz.BindRec[scalaz.Scalaz.Id]
       val res0 =
           ^

scala> Monad[Lambda[X => X]]
Monad[Lambda[X => X]]
<console>:13: warning: Unused import
       import Scalaz._
                     ^
res1: scalaz.Monad[[X]X] = scalaz.IdInstances$$anon$1@4037c5b8

It's an aliasing issue.

@djspiewak
Copy link
Owner

It appears that some witchcraft is summoning cats.catsInstancesForId despite not having been imported. I really don't know what could be causing this other than a bug in scalac.

@djspiewak
Copy link
Owner

I figured it out. Both scalaz and cats Id type gets automagically summoned instances. Behold:

scalaz.Monad[scalaz.Id.Id]
cats.Monad[cats.Id]

Both those lines compile with no imports. So this is why things are ambiguous: both monads are in scope, and shims makes them equivalent.

But, "how?!" you ask. Good question, intrepid reader! This gist shows a condensed example of the phenomenon in play here: https://gist.github.com/8680f463d4e2e4a8aa8a9e0bb7999ce0 The implicit scope for resolving a given type includes all companions of components of the type. As it turns out, one of the components of a type is the package itself, and the package object is considered part of the package scope. As you may recall from Shapeless's Poly, objects are their own companions, and package objects are objects (mostly). So in other words, the fact that both scalaz and cats put their idInstance implicits into the package prevents us from eliminating those instances from scope.

I would consider this to be a pretty significant design flaw in both frameworks, but in fairness, this is a really really obscure corner of implicit scoping.

In the meantime, I'm not really sure if there's anything shims can do here. In theory, I'd really like shims implicits to be "magically lowest" priority in that they are only selected when no other implicits are applicable, but I don't have a handy trick for achieving that.

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

No branches or pull requests

2 participants