В scala бесформенной библиотеке есть ли собственный способ преобразования типа продукта (HList / Generic / NamedGeneri c) в тип синглтона int, равный его арности? - PullRequest
1 голос
/ 09 марта 2020

Я пытаюсь выяснить, как связать функцию (одноэлементный тип) бесформенного с другим (HList), предполагая, что мы хотим получить обобщенный c тип Vector, который содержит информацию о арности входной вектор:

package com.tribbloids.spike.shapeless_spike.shapesafe

import shapeless.{Generic, Witness}

import scala.language.implicitConversions

trait Vector[W <: Witness.Lt[Int]] extends Serializable {

  def witness: W

  def <*>(that: Vector[W]): Vector[W] = ???
}

object Vector {

  case class Impl[W <: Witness.Lt[Int]](witness: W) extends Vector[W] {}

  def zeros[W <: Witness.Lt[Int]](witness: W): Impl[W] = Impl(witness)

  object Attempt1 {

    def values(v: (Double)) = Impl(Witness(1))
    def values(v: (Double, Double)) = Impl(Witness(2))
    def values(v: (Double, Double, Double)) = Impl(Witness(3))

    Vector.zeros(Witness(2)) <*> values(2.0, 3.0)

    Vector.zeros(Witness(3)) <*> values(2.0, 3.0) // breaks as expected
  }
}

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

  object Attempt2 {
    type HInts = Int :: HInts

    def values[T <: Product](v: T)(implicit aux: Generic.Aux[T, HInts]) = {
      ???
    }
  }

Однако, несмотря на то, что оба они являются очень зрелой бесформенной функцией, похоже, нет документации, объясняющей, как включить такой вывод алгебры c.

Is Есть ли простой способ заменить все мои неявные функции на достаточно общий оператор? Я сейчас использую scala -2.12 + бесформенный 2.3.

Большое спасибо за информацию.

1 Ответ

0 голосов
/ 11 марта 2020

Если все, что вам нужно, это

  • способность express размер продукта в коде в виде литерала
  • принудительное применение этого размера во время компиляции

это гораздо проще, если вы используете Nat вместо Witness.Lt[Int]:

~ amm
Loading...
Welcome to the Ammonite Repl 2.0.4 (Scala 2.13.1 Java 1.8.0_242)
@  import $ivy.`com.chuusai::shapeless:2.3.3`, shapeless._
import $ivy.$                             , shapeless._

@ {
  trait OfSize[A, N <: Nat]
  object OfSize {

    implicit def evidence[A, Repr <: HList, N <: Nat](
      implicit gen: Generic.Aux[A, Repr],
      length: shapeless.ops.hlist.Length.Aux[Repr, N]
    ): OfSize[A, N] = new OfSize[A, N] {}
  }
  }
defined trait OfSize
defined object OfSize

@ def needSized[A, N <: Nat](a: A, n: N)(implicit ev: A OfSize N) = "OK"
defined function needSized

@ case class Test(a: String, b: Int)
defined class Test

@ needSized(Test("a", 0), Nat(3))
cmd4.sc:1: could not find implicit value for parameter ev: ammonite.$sess.cmd1.OfSize[ammonite.$sess.cmd3.Test,shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]
val res4 = needSized(Test("a", 0), Nat(3))
                    ^
Compilation Failed

@ needSized(Test("a", 0), Nat(2))
res4: String = "OK"

@

Я считаю, что Nat в некоторых for будет доступен в Shapeless 3, так что это будет более переносимый подход, чем опираясь на свидетелей. Однако если вы хотите использовать Witness, то, я думаю, тогда потребуется предоставить собственный макрос для преобразования Witness в Nat, а затем использовать существующие бесформенные утилиты.

...