Подтвердить что ты не робот

Передача неограниченной расширяемой записи в функцию

Я пытаюсь изучить Shapeless (используя версию 2.10.2). Я создал очень простую расширяемую запись:

val rec1 = ("foo" ->> 42) :: HNil

В соответствии с REPL это имеет тип

shapeless.::[Int with shapeless.record.KeyTag[String("foo"),Int],shapeless.HNil]

Я пытаюсь определить простую функцию:

def fun(x: ::[Int with KeyTag[String("foo"), Int], HNil]) = x("foo")

но он даже не компилируется. Я не могу использовать String ( "foo" ) в объявлении типа и получить ошибку.

У меня есть два вопроса:

  • Как я могу указать тип расширяемой записи в моем коде?
  • При работе с записями с большим количеством полей длина и сложность объявления типа будут неуправляемы. Есть ли способ создать псевдоним для типа, учитывая конкретный экземпляр записи или какой-либо другой способ обхода?

ИЗМЕНИТЬ

Я обнаружил, что:

val rec1 = ("foo" ->> 42) :: HNil
val rec2 = ("foo" ->> 43) :: HNil
var x = rec1
x = rec2

работает хорошо. В заключение rec1, rec2 и x имеют один и тот же тип. Я просто не знаю, как выразить этот тип кода!

4b9b3361

Ответ 1

Здесь нечто более общее, что, я думаю, может ответить на ваш вопрос. Предположим, мы хотим написать метод, который будет работать с любой записью с помощью клавиши "foo". Мы можем использовать комбинацию свидетеля и селектора:

import shapeless._, record._, syntax.singleton._

val w = Witness("foo")

def fun[L <: HList](xs: L)(implicit sel: ops.record.Selector[L, w.T]) = xs("foo")

И затем:

scala> fun(("foo" ->> 42) :: HNil)
res0: Int = 42

Или:

scala> fun(("bar" ->> 'a) :: ("foo" ->> 42) :: HNil)
res1: Int = 42

Если мы действительно хотели разрешить записи без каких-либо других полей, мы могли бы написать следующее:

def fun(l: Int with KeyTag[w.T, Int] :: HNil) = l("foo")

Но это несколько противоречит тому, как обычно используются записи.

Мы должны определить свидетеля именно потому, что Scala 2.10 не предоставляет никакого способа напрямую ссылаться на одноэлементный тип - см., например, мою вилку в Alois Cochard Shona для обсуждения.

Я добавлю в качестве окончательного заявления об отказе от ответственности, что я только сейчас знакомлюсь с Shapeless 2.0 самостоятельно, но я не думаю, что даже Майлс достаточно магичен, чтобы обойти это ограничение.

Ответ 2

С бесформенной версии 2.1.0 существует новый синтаксис для выражения типов записей:

scala> :paste
// Entering paste mode (ctrl-D to finish)

import shapeless._
import shapeless.record._
import shapeless.syntax.singleton._

def fun(x: Record.`"foo" -> Int`.T) = x("foo")

// Exiting paste mode, now interpreting.

import shapeless._
import shapeless.record._
import shapeless.syntax.singleton._
fun: (x: shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.HNil])Int

scala> fun( ("foo" ->> 42) :: HNil )
res2: Int = 42

scala> fun( ("foo" ->> 42) :: ("bar" ->> 43) :: HNil )
<console>:30: error: type mismatch;
 found   : shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.::[Int with shapeless.labelled.KeyTag[String("bar"),Int],shapeless.HNil]]
 required: shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.HNil]
       fun( ("foo" ->> 42) :: ("bar" ->> 43) :: HNil )

Но селектор, вероятно, лучший подход для использования в случае использования.