Search code examples
scalatypesdottyscala-3polymorphic-functions

Type lambda with higher kind


In Dotty given the following:

object Domain {
  final case class Create(name: String) extends BaseCreate[Create] {
    override type Model = Domain
    override def service[F[_]](client: KeystoneClient[F]): CrudService[F, Domain, Create] = client.domains
  }
}
case class Domain(id: String)

class CrudService[F[_], Model, Create]
final class Domains[F[_]] extends CrudService[F, Domain, Domain.Create]

class KeystoneClient[F[_]] {
  val domains = new Domains[F]
}

trait BaseCreate[Create <: BaseCreate[Create]] {                                                                                                        
  type Model
  def service[F[_]](client: KeystoneClient[F]): CrudService[F, Model, Create]
}

I want to "simplify" BaseCreate such that I can implement Create like this:

final case class Create(name: String) extends BaseCreate.Aux[Domain, Create](_.domains)

I've tried:

object BaseCreate {
  abstract class Aux[M, Create](f: [F[_]] =>> KeystoneClient[F] => CrudService[F, M, Create]) extends BaseCreate[Create] {                                        
    type Model = M
    def service[F[_]](client: KeystoneClient[F]): CrudService[F, Model, Create] = f(client)
  }
}

But the compiler complains with:

Missing type parameter for [F[_$5]] =>> KeystoneClient[F] => CrudService[F, M, Create]

Which doesn't make sense to me. Whats the right syntax, if there is one?

Link to scastie with this code

PS: I don't want to introduce any more type parameters to BaseCreate. Especially the F since that would mean the class Domain.Create would need to be final case class Create[F[_]] which is not at all the intended solution.


Solution

  • I guess you confused type lambdas with polymorphic functions.

    A type lambda maps types to types, a polymorhic function maps values to values just type of its argument can vary.

    For type lambdas you should use =>>, for polymorhic functions you should use ordinary => (twice i.e. [A] => (a: A) => f(a)).

    So it's supposed to be

    object BaseCreate {
      abstract class Aux[M, Create](f: [F[_]] => KeystoneClient[F] => CrudService[F, M, Create]) extends BaseCreate[Create] {
        type Model = M
        def service[F[_]](client: KeystoneClient[F]): CrudService[F, Model, Create] = f(client)
      }
    }
       
    final case class Create(name: String) extends BaseCreate.Aux[Domain, Create](
      [F[_]] => (client: KeystoneClient[F]) => client.domains
    )
    

    but it doesn't compile in Dotty 0.28.0-bin-20200920-e99793e-NIGHTLY with error

    Found:    Object with PolyFunction {...}
    Required: PolyFunction{
      apply: 
        [F[_$6]]
          (x$1: App.KeystoneClient[F]): 
            App.CrudService[F, App.Domain, App.Create]
    }
        [F[_]] => (client: KeystoneClient[F]) => client.domains
    

    Seems to be a bug.