首页 > 解决方案 > Scala compiler infers overly specific type with complex type parameter

问题描述

I'm writing an FRM, with the fields abstracted over F[_].

trait Query[A]
case class Field[A](name: String)
case class DBTable[T[_[_]]](fields: T[Field]) extends Query[T[Field]]

// example table
case class PersonalInfo[F[_]](name: F[String], age: F[Int])

I want to perform query rewrites using recursion schemes, so I need to define a pattern functor,

trait QueryF[A, B]
case class DBTableF[T[_[_]], B](fields: T[Field]) extends QueryF[T[Field], B]

and then a coalgebra to lift the Query into it:

type Coalgebra[F[_], A] = A => F[A]

def coalg[A]: Coalgebra[QueryF[A, ?], Query[A]] = {
    case DBTable(fields) => DBTableF(fields)
}

This works, but defining an algebra to convert in the opposite direction does not:

type Algebra[F[_], A] = F[A] => A

def alg[A]: Algebra[QueryF[A, ?], Query[A]] = {
  case DBTableF(value) => DBTable[A](value)
}

the alg function throws a compiler error, saying constructor of type controllers.thing.DBTableF[T,B] cannot be uniquely instantiated to expected type controllers.thing.QueryF[?,controllers.thing.Query[?]

标签: scalatypesfunctional-programminghigher-kinded-types

解决方案


How about this here:

import scala.language.higherKinds

trait Query[A]
case class Field[A](name: String)
case class DBTable[T[_[_]]](fields: T[Field]) extends Query[T[Field]]

trait QueryF[A, B]
case class DBTableF[T[_[_]], B](fields: T[Field]) extends QueryF[T[Field], B]

type Coalgebra[F[_], A] = A => F[A]

def coalg[A]: Coalgebra[({ type L[X] = QueryF[A, X]})#L, Query[A]] = {
    case DBTable(fields) => DBTableF(fields)
}

type Algebra[F[_], A] = F[A] => A

def alg[A]: Algebra[({ type L[X] = QueryF[A, X]})#L, Query[A]] = {
  case dbtf: DBTableF[t, b] => DBTable(dbtf.fields)
}

or alternatively, with the last case replaced by:

  case dbtf: DBTableF[t, b] => DBTable[t](dbtf.fields)

if this is a bit clearer.

Both variants compile with -Ypartial-unification on 2.12.6.


推荐阅读