Scala контекстные границы

Используя границы контекста в Scala, вы можете делать такие вещи, как

trait HasBuild[T] {
  def build(buildable: T): Something
}

object Builders {
  implict object IntBuilder extends HasBuild[Int] {
    override def build(i: Int) = ??? // Construct a Something however appropriate
  }
}

import Builders._
def foo[T: HasBuild](input: T): Something = implicitly[HasBuild[T]].build(1)
val somethingFormInt = foo(1)

Или просто

val somethingFromInt = implicitly[HasBuild[Int]].build(1)

Как я мог выразить тип Seq любых элементов, которые имеют соответствующие неявные HasBuild объект в области? Возможно ли это без особых волшебных и внешних библиотек?

Seq[WhatTypeGoesHere] - Я должен быть в состоянии найти подходящий HasBuild для каждого элемента

Это, очевидно, не компилируется:

val buildables: Seq[_: HasBuild] = ???

2 ответа

Решение

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

Что вы можете сделать:

case class HasHasBuild[A](value: A)(implicit val ev: HasBuild[A])
object HasHasBuild {
  implicit def removeEvidence[A](x: HasHasBuild[A]): A = x.value
  implicit def addEvidence[A: HasBuild](x: A): HasHasBuild[A] = HasHasBuild(x)
}

и сейчас (при условии, что вы добавляете HasBuild[String] для демонстрации):

val buildables: Seq[HasHasBuild[_]] = Seq(1, "a")

компилируется, но

val buildables1: Seq[HasHasBuild[_]] = Seq(1, "a", 1.0)

не делает. Вы можете использовать методы с неявным HasBuild параметры, когда у вас есть только HasHasBuild:

def foo1[A](x: HasHasBuild[A]) = {
  import x.ev // now you have an implicit HasBuild[A] in scope
  foo(x.value)
}

val somethings: Seq[Something] = buildables.map(foo1(_))

Перво-наперво, вопреки некоторым комментариям, вы полагаетесь на границы контекста. Запрос неявного экземпляра класса типа для T это то, что вы называете "привязанным к контексту".

То, что вы хотите, достижимо, но не тривиально и, конечно, не без других библиотек.

import shapeless.ops.hlist.ToList
import shapeless._
import shapeless.poly_

object builder extends Poly1 {
  implicit def caseGeneric[T : HasBuilder] = {
    at[T](obj => implicitly[HasBuilder[T]].build(obj))
  }
}

class Builder[L <: HList](mappings: L) {
  def build[HL <: HList]()(
    implicit fn: Mapper.Aux[builder.type, L, HL],
    lister: ToList[Something]
  ) = lister(mappings map fn)

  def and[T : HasBuilder](el: T) = new Builder[T :: L](el :: mappings)
}


object Builder {
  def apply[T : HasBuilder](el: T) = new Builder(el :: HNil)
}

Теперь вы можете сделать что-то вроде:

Builder(5).and("string").build()

Это вызовет build методы из всех отдельных экземпляров классов типа implcit и предоставляют вам список результатов, где каждый результат имеет тип Something, Он опирается на тот факт, что все методы сборки имеют нижнюю верхнюю границу SomethingНапример, согласно вашему примеру:

trait HasBuild[T] {
  def build(buildable: T): Something
}
Другие вопросы по тегам