Использование включения параметров универсального типа во вложенные полиморфные значения

Можно ли переписать следующий пример, используя только полиморфные функции без TypeTags? Пример состоит из класса A[T], который имеет метод matches, возвращающий true при применении к экземпляру A с параметром того же типа T и false, если этот параметр типа имеет другое значение. Затем matches дважды сопоставляется с h-списком l из A[T], что приводит к h-списку вложенных h-списков, содержащих результаты сопоставления каждого элемента l с другими:

import scala.reflect.runtime.universe._
import shapeless._

class A[T: TypeTag]{
  object matches extends Poly1 {
    implicit def default[U: TypeTag] = at[A[U]]{ _ => typeOf[U] <:< typeOf[T] }
  }
}

val l = new A[Int] :: new A[String] :: new A[Boolean] :: HNil

object matcher extends Poly1 {
  implicit def default[T] = at[A[T]]{ a => l map a.matches }
}

l map matcher

Каждому элементу соответствует одно совпадение, т.е. результат:

(true :: false :: false :: HNil) ::
(false :: true :: false :: HNil) ::
(false :: false :: true :: HNil) :: HNil

Когда я пытаюсь переписать пример без TypeTags, matches всегда использует no регистр и возвращает false:

import shapeless._

class A[T]{
    object matches extends Poly1 {
        implicit def yes = at[A[T]]{_ => true}
        implicit def no[U] = at[U]{_ => false}
    }
}

val l = new A[Int] :: new A[String] :: new A[Boolean] :: HNil

object matcher extends Poly1 {
    implicit def default[T] = at[A[T]]{ a => l map a.matches }
}

l map matcher

Результат:

(false :: false :: false :: HNil) ::
(false :: false :: false :: HNil) ::
(false :: false :: false :: HNil) :: HNil

Можно ли переписать этот пример без TypeTags и получить тот же результат, что и в первом случае?


person ps_ttf    schedule 18.11.2013    source источник


Ответы (1)


Похоже, вы действительно хотите иметь возможность частично применять функции более высокого ранга, чтобы чисто решить эту проблему. Это невозможно с помощью какого-либо красивого синтаксиса из коробки, но я однажды написал код, чтобы помочь сделать это немного проще (учтите, что это все 1.2.4):

import shapeless._

trait ApplyMapper[HF, A, X <: HList, Out <: HList] {
  def apply(a: A, x: X): Out
}

object ApplyMapper {
  implicit def hnil[HF, A] = new ApplyMapper[HF, A, HNil, HNil] {
    def apply(a: A, x: HNil) = HNil
  }
  implicit def hlist[HF, A, XH, XT <: HList, OutH, OutT <: HList](implicit
    pb: Poly.Pullback2Aux[HF, A, XH, OutH],
    am: ApplyMapper[HF, A, XT, OutT]
  ) = new ApplyMapper[HF, A, XH :: XT, OutH :: OutT] {
    def apply(a: A, x: XH :: XT) = pb(a, x.head) :: am(a, x.tail)
  }
}

См. Ответ по ссылке выше для некоторого контекста.

Это позволяет вам написать следующее:

class A[T]

object matches extends Poly2 {
  implicit def default[T, U](implicit sub: U <:< T = null) =
    at[A[T], A[U]]((_, _) => Option(sub).isDefined)
}

object mapMatcher extends Poly1 {
   implicit def default[T, X <: HList, Out <: HList](
     implicit am: ApplyMapper[matches.type, A[T], X, Out]
   ) = at[(A[T], X)] { case (a, x) => am(a, x) }
}

val l = new A[Int] :: new A[String] :: new A[Boolean] :: HNil

Возможно, есть более приятное решение, но на данный момент у меня есть только минута, чтобы ответить, и оно работает как есть:

scala> l.zip(l mapConst l).map(mapMatcher).toList.foreach(println)
true :: false :: false :: HNil
false :: true :: false :: HNil
false :: false :: true :: HNil

По желанию.

person Travis Brown    schedule 18.11.2013