It seems Scala fails to find correct implicits when using type aliases:
case class MyData[A]()
implicit val myFunctor: Functor[MyData] = new Functor[MyData] {
override def map[A, B](fa: MyData[A])(f: A => B): MyData[B] = MyData[B]()
}
def showOption[A](opt: Option[A]): String = opt match {
case Some(a) => a.toString
case None => "nothing"
}
type T[A] = MyData[Option[A]]
val a: MyData[Option[Int]] = MyData()
val b: MyData[String] = a map showOption <-- compiles
val c: T[Int] = MyData()
val d: MyData[String] = c map showOption <-- compilation error
Similar code written in Haskell compiles fine:
{-# LANGUAGE ScopedTypeVariables #-}
data MyData a = MyData
instance Functor MyData where
fmap _ _ = MyData
showMaybe :: (Show a) => Maybe a -> String
showMaybe (Just a) = show a
showMaybe Nothing = "nothing"
type T a = MyData (Maybe a)
main :: IO ()
main =
let (a :: MyData (Maybe Int)) = MyData
(b :: MyData String) = fmap showMaybe a
(c :: T Int) = MyData
(d :: MyData String) = fmap showMaybe c
in return ()
This is a known issue with dealiasing. Sometimes scalac just isn't particularly good about throwing away the hidden identity of the type alias and that can result in situations like this. In practice, this usually isn't a problem since you're often going through abstractions (e.g. def foo[F[_]: Functor]) which will force the dealiasing by definition, but sometimes you can see it. State/StateT/IndexedStateT can exhibit this issue on occasion.
Is there any workaround? I have several nested types in my project, and type signatures mind be much nicer if i'll be able to use type aliases.
Type aliases aren't as innocent as they look … in this case you're changing the shape from F[G[?]] to F[?], which is significant in Scala. In fact for several years we relied on this behavior to work around legendary issue SI-2712.
As far as I know the only general solution is "don't do that".
As far as I know the only general solution is "don't do that".
"Don't do that" or "always go through some abstraction F[_]". I generally find myself structuring my code according to the latter.
What do you mean by going through some abstraction? Е.g. having function foo[T]: MyData[Option[T]]how it could be modified to use shorter abbreviation for MyData[Option[T]] other than by using type alias?
Most helpful comment
"Don't do that" or "always go through some abstraction
F[_]". I generally find myself structuring my code according to the latter.