3

I have these types: SomeTypeClass
A higher kinded type which has one type parameter of kind * => * => *

trait SomeTypeClass[P[_, _]] {
    def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                              (pab: P[A, B])
                              (implicit ev: Strong[P],
                              ev2: Choice[P],
                              ev3: Applicative[F]): P[S, T]
}

Target which accepts three type parameters:
type constructor F[_] and two polymorphic types A, B

case class Target[F[_], A, B](f: A => F[B])

I want to implement an instance of SomeTypeClass of Target.
I am using the kind-projector plugin in order to create a partially applied type.
My desired method signature should be:

implicit def instance: SomeTypeClass[Target[F, *, *]] = new SomeTypeClass[Target[F, *, *]] {
  override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                                      (pab: Target[F, A, B])
                                      (implicit ev: Strong[Target[F, *, *]], 
                                       ev2: Choice[Target[F, *, *]], 
                                       ev3: Applicative[F]): Target[F, S, T] = ???
}

I've tried using this syntax using two star parameters:

 implicit def instance[F[_]]: SomeTypeClass[Target[F, *, *]] = new SomeTypeClass[Target[F, *, *]] {
    override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                                     (pab: Target[F, A, B])
                                     (implicit ev: Strong[Target[F, *, *]],
                                      ev2: Choice[Target[F, *, *]],
                                      ev3: Applicative[F]): Target[F, S, T] = ???
}

But the F[_] declared at the instance level shadows the F[_] declared at the test method (I want them to be the same F), so I've moved to the λ syntax and got two different unwanted results.

The first one using λ[(F, A, B) => Target[F, A, B]] generated for the pab paramter, pab: Target[A, B, B] instead of pab: Target[F, A, B] and also for the return type Target[S, T, B] instead of Target[F, S, T]

The second one using the F at the end of the triple type lambda parameters (why???)
λ[(A, B, F) => Target[F, A, B]] generated the correct types for the pab parameter and the return type, but for each one of the implicit parameters the type Strong[λ[(A, B, F) => Target[F, A, B]]] instead of Strong[Target[F, *, *]]]

The full code:

  import cats.Applicative
  import cats.arrow.{Choice, Strong}

  final case class Target[F[_], A, B](f: A => F[B])

  trait SomeTypeClass[P[_, _]] {
    def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                              (pab: P[A, B])
                              (implicit ev: Strong[P],
                               ev2: Choice[P],
                               ev3: Applicative[F]): P[S, T]
  }

  object SomeTypeClass {
    implicit def instance1: SomeTypeClass[λ[(F, A, B) => Target[F, A, B]]] = new SomeTypeClass[λ[(F, A, B) => Target[F, A, B]]] {
      override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                                         (pab: Target[A, B, B])
                                         (implicit ev: Strong[Target],
                                          ev2: Choice[Target],
                                          ev3: Applicative[F]): Target[S, T, B] = ???
    }

    implicit def instance2: SomeTypeClass[λ[(A, B, F) => Target[F, A, B]]] = new SomeTypeClass[λ[(A, B, F) => Target[F, A, B]]] {
      override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                                         (pab: Target[F, A, B])
                                         (implicit ev: Strong[λ[(A, B, F) => Target[F, A, B]]],
                                          ev2: Choice[λ[(A, B, F) => Target[F, A, B]]],
                                          ev3: Applicative[F]): Target[F, S, T] = ???
    }
  }

Can I achieve the desired syntax using this plugin?
Why does the plugin generate different types for different order of type lambda's 'parameters'?

Sagi
  • 8,972
  • 3
  • 33
  • 41
  • I don't understand what you mean by these uses of `λ` "generating the correct types" for the parameter. Neither of these compile, and I wouldn't expect either to. If you want to use a type lambda in the context of `SomeClass[...]` you'll definitely need one with two `*` parameters. – Travis Brown Dec 11 '19 at 21:30
  • @TravisBrown, I've updated the question with the two star parameters example – Sagi Dec 11 '19 at 21:52
  • But `λ[(F, A, B) => Target[F, A, B]]` takes three parameters, so you definitely shouldn't be able to use it in any context where a `P[_, _]` is expected. If your final code block compiles at all that's a bug. – Travis Brown Dec 12 '19 at 10:07
  • @TravisBrown I've also tried λ[F[_] => Target[F, *, *]] but I also got unwanted results – Sagi Dec 12 '19 at 10:24

1 Answers1

2

If I understood

But the F[_] declared at the instance level shadows the F[_] declared at the test method (I want them to be the same F)

correctly, you want your instance for SomeTypeClass[Target[...]] to fix the F[_] parameter of test. But that's simply not possible with this test type signature. Once you have (for example)

val inst = implicitly[SomeTypeClass[Target[...]]

you can call

val res1 = inst.test[List, ...]
val res2 = inst.test[Option, ...]

Type lambdas don't offer a way around this problem. You need to either move F[_] parameter to SomeTypeClass or implement

implicit def instance[F[_]]: SomeTypeClass[Target[F, *, *]] = new SomeTypeClass[Target[F, *, *]] {
  override def test[G[_], S, T, A, B](f: (A => G[B]) => S => G[T])
                                     (pab: Target[F, A, B])
                                     (implicit ev: Strong[Target[F, *, *]],
                                      ev2: Choice[Target[F, *, *]],
                                      ev3: Applicative[G]): Target[G, S, T] = ???
}

which I expect is impossible as you can't pass pab.f to f.

EDIT: the type of wander

class (Choice p, Strong p) => Traversing p where
  traverse' :: Traversable f => p a b -> p (f a) (f b)
  traverse' = wander traverse

  wander :: (forall f. Applicative f => (a -> f b) -> s -> f t) -> p a b -> p s t
  wander f pab = dimap (\s -> Baz $ \afb -> f afb s) sold (traverse' pab)

is a rank-2 type which aren't supported in Scala directly; instead you need to introduce a helper (which can't just be a type alias as it is in Control.Lens.Type)

trait Traversal[S, T, A, B] {
  def apply[F[_]: Applicative](f: A => F[B]): S => F[T]
}

Then

trait Traversing[P[_, _]] extends Strong[P] with Choice[P] {
  def wander[S, T, A, B](t: Traversal[S, T, A, B], pab: P[A, B]): P[S, T]
}

implicit def instance[F[_]: Applicative]: Traversing[Target[F, *, *]] = new Traversing[Target[F, *, *]] {
  def wander[S, T, A, B](t: Traversal[S, T, A, B], pab: Target[F, A, B]): Target[F, S, T] = Target(t(pab.f))
  // define Strong and Choice methods too
}

should work. (Though I am not sure this is the cats way to deal with Strong and Choice requirements.)

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • So basically I can not implement this? instance testSomeTypeClass :: Applicative f => SomeTypeClass (Target f) where test t (Target f) = Target (t f) – Sagi Dec 12 '19 at 11:17
  • What does your `SomeTypeClass` (type of `test` in particular) look like in Haskell? – Alexey Romanov Dec 12 '19 at 11:33
  • Looks like a lens – Sagi Dec 12 '19 at 11:38
  • test:: Applicative f => (a -> f b) -> s -> f t -> p a b -> p s t – Sagi Dec 12 '19 at 11:40
  • Then you have shadowing in Haskell as well, `f` in `test` is unrelated to `f` in `instance Applicative f => SomeTypeClass (Target f)` (assuming `class SomeTypeClass p where test :: ...` and not `SomeTypeClass p f`). So `test t (Target f) = Target (t f)` shouldn't type-check, unless I am missing something. – Alexey Romanov Dec 12 '19 at 11:48
  • And this doesn't compile if I try it, as expected: "Couldn't match expected type ‘f t -> Target m a b -> Target m s t’ with actual type ‘Target f0 a1 b0’" in `wander f (Target amb) = Target (f amb)`. – Alexey Romanov Dec 12 '19 at 13:22
  • Though this `wander` corresponds to `def test[F[_], S, T, A, B](f: A => F[B])(x: S, y: F[T], pab: P[A, B])(implicit ...): P[S, T]` in Scala, not the signature you have in the question. – Alexey Romanov Dec 12 '19 at 13:24
  • can you please write down the typeclass of Traversing (just the wander) which corresponds to SomeTypeClass and the signature of Star which corresponds to Target from this Haskell implementation: http://hackage.haskell.org/package/profunctors-5.5.1/docs/src/Data.Profunctor.Traversing.html#Traversing. Thanks. – Sagi Dec 12 '19 at 14:07
  • Thank you very much, I wish I could upvote your answer more than one time – Sagi Dec 12 '19 at 20:01
  • Sure did. Even before you have edited your answer with the Traversal. Just for the effort you showed. Thanks Again – Sagi Dec 12 '19 at 22:48
  • @Sagi No, you haven't yet. Accepting is separate from upvoting, you need to click on a checkmark under the answer. https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work – Alexey Romanov Dec 13 '19 at 07:18