Хорошо, справедливое предупреждение: это продолжение моего смешного вопроса с прошлой недели. Хотя я думаю, что этот вопрос не такой уж смешной. В любом случае, здесь идет:
Предыдущий смешной вопрос:
Предположим, что у меня есть базовый признак T
с подклассами A
, B
и C
, я могу, например, объявить коллекцию Seq[T]
, которая может содержать значения типа A
, B
и C
. Чтобы сделать подтипирование более явным, используйте синтаксис типа Seq[_ <: T]
.
Теперь вместо этого предположим, что у меня есть typeclass TC[_]
с членами A
, B
и C
(где "член" означает, что компилятор может найти некоторые TC[A]
и т.д. в неявной области). Как и выше, я хочу объявить коллекцию типа Seq[_ : TC]
, используя синтаксис с привязкой к контексту.
Это не является законным Scala, и попытка эмулировать может заставить вас чувствовать себя плохой человек. Помните, что связанный с контекстом синтаксис (при использовании правильно!) Desugars в список неявных параметров для определяемого класса или метода, что здесь не имеет никакого смысла.
Новое помещение:
Итак, допустим, что экземпляры typeclass (т.е. неявные значения) не могут быть и речи, и вместо этого нам нужно использовать неявные преобразования в этом случае. У меня есть какой-то тип V
(предполагается, что "v" означает "вид", fwiw) и неявные преобразования в области A => V
, B => V
и C => V
. Теперь я могу заполнить Seq[V]
, несмотря на то, что A
, B
и C
в противном случае не связаны.
Но что, если я хочу набор вещей, которые неявно конвертируются как в представления V1
, так и V2
? Я не могу сказать Seq[V1 with V2]
, потому что мои неявные преобразования не магически суммируются таким образом.
Пересечение неявных преобразований?
Я решил свою проблему следующим образом:
// a sort of product or intersection, basically identical to Tuple2
final class &[A, B](val a: A, val b: B)
// implicit conversions from the product to its member types
implicit def productToA[A, B](ab: A & B): A = ab.a
implicit def productToB[A, B](ab: A & B): B = ab.b
// implicit conversion from A to (V1 & V2)
implicit def viewsToProduct[A, V1, V2](a: A)(implicit v1: A => V1, v2: A => V2) =
new &(v1(a), v2(a))
Теперь я могу написать Seq[V1 & V2]
как босс. Например:
trait Foo { def foo: String }
trait Bar { def bar: String }
implicit def stringFoo(a: String) = new Foo { def foo = a + " sf" }
implicit def stringBar(a: String) = new Bar { def bar = a + " sb" }
implicit def intFoo(a: Int) = new Foo { def foo = a.toString + " if" }
implicit def intBar(a: Int) = new Bar { def bar = a.toString + " ib" }
val s1 = Seq[Foo & Bar]("hoho", 1)
val s2 = s1 flatMap (ab => Seq(ab.foo, ab.bar))
// equal to Seq("hoho sf", "hoho sb", "1 if", "1 ib")
Неявные преобразования из String
и Int
для ввода Foo & Bar
возникают при заполнении последовательности, а затем при вызове foobar.foo
и затем неявные преобразования от Foo & Bar
до Foo
и Bar
foobar.bar
.
Нынешний смешной вопрос (ы):
- Кто-нибудь реализовал этот шаблон раньше, или я первый идиот, чтобы сделать это?
- Есть ли более простой способ сделать это, о котором я слепо пропустил?
- Если нет, то как мне применить более общую сантехнику, чтобы я мог написать
Seq[Foo & Bar & Baz]
? Это похоже на работу дляHList
... - Дополнительный мега-бонус: при реализации более общей сантехники я могу ограничить типы уникальными? Например, я хотел бы запретить
Seq[Foo & Foo]
.
Приложение сбоя:
Моя последняя попытка (gist). Не страшно, но есть две вещи, которые мне не нравятся:
- Синтаксис
Seq[All[A :: B :: C :: HNil]]
(я хочу, чтобы материалHList
был непрозрачным и предпочиталSeq[A & B & C]
) - Явная аннотация типа (
abc[A].a
), необходимая для преобразования. Похоже, вы можете либо иметь вывод типа, либо неявные преобразования, но не оба... Я никак не мог понять, как его избежать.