簡體   English   中英

使用未知類型折疊HList

[英]Fold over HList with unknown Types

我有一種復雜的類型層次結構,但要分解它有兩個基本特征: Convertable Conversion[A <: Convertable, B <: ConvertableConversion[A <: Convertable, B <: Convertable可轉換,例如,有一個轉換可以將Mealy自動機轉換為摩爾 -自動機。 每個Conversion[A,B]都有一個convert(automaton: A) : B方法。

現在我想介紹智能轉換的概念,它基本上是一個普通轉換列表,將一個接一個地執行。 因此,我引入了一個AutoConversion特性,擴展了一個轉換,它具有一個val path : HList參數,用於表示轉換鏈,並且應該實現convert方法,以便AutoConversions只需提供要采取的實際轉換列表。 我認為你可以通過path fold來實現這一點,所以這是我的第一次嘗試:

package de.uni_luebeck.isp.conversions

import shapeless._
import shapeless.ops.hlist.LeftFolder

trait AutoConversion[A <: Convertable, B <: Convertable] extends Conversion[A, B] {
  val path: HList

  object combiner extends Poly {
      implicit def doSmth[C <: Convertable, D <: Convertable] = 
         use((conv : Conversion[C, D] , automaton : C) => conv.convert(automaton))

}

  override def convert(startAutomaton: A): B = {
    path.foldLeft(startAutomaton)(combiner)
  }
}

這不起作用,因為沒有找到隱含的文件夾,所以我猜我必須為編譯器提供更多類型信息,但不知道在哪里

你是否需要更多的類型信息,一般來說,如果你有一個HList作為靜態類型的值,你可能需要改變你的方法。 如果您只知道HList是一個HList (除了為它添加值之外),那么HList基本上沒什么可以做的,而且您通常只會將HList寫為類型約束。

在你的情況下,你所描述的是一種類型對齊的序列。 在你采用這種方法之前,我建議你真的確定你真的需要。 關於函數(以及類似函數的類型,如Conversion )的一個好處是它們組成:你有一個A => B和一個B => C並且你將它們組成A => C並且可以忘記B永遠。 你得到一個漂亮干凈的黑盒子,這通常正是你想要的。

但是,在某些情況下,能夠以能夠反映管道部分的方式組合類似函數的東西會很有用。 我會假設這是其中一種情況,但你應該為自己確認一下。 如果不是,那么你很幸運,因為即將發生的事情有點混亂。

我會假設這些類型:

trait Convertable

trait Conversion[A <: Convertable, B <: Convertable] {
  def convert(a: A): B
}

我們可以定義一個類型類,它見證特定的HList由一個或多個類型HList轉換組成:

import shapeless._

trait TypeAligned[L <: HList] extends DepFn1[L] {
  type I <: Convertable
  type O <: Convertable
  type Out = Conversion[I, O]
}

L包含有關管道的所有類型信息, IO是其端點的類型。

接下來我們需要這個類型類的實例(請注意,這必須與上面的特征一起定義,以便兩個人同伴):

object TypeAligned {
  type Aux[L <: HList, A <: Convertable, B <: Convertable] = TypeAligned[L] {
    type I = A
    type O = B
  }

  implicit def firstTypeAligned[
    A <: Convertable,
    B <: Convertable
  ]: TypeAligned.Aux[Conversion[A, B] :: HNil, A, B] =
    new TypeAligned[Conversion[A, B] :: HNil] {
      type I = A
      type O = B
      def apply(l: Conversion[A, B] :: HNil): Conversion[A, B] = l.head
    }

  implicit def composedTypeAligned[
    A <: Convertable,
    B <: Convertable,
    C <: Convertable,
    T <: HList
  ](implicit
    tta: TypeAligned.Aux[T, B, C]
  ): TypeAligned.Aux[Conversion[A, B] :: T, A, C] =
    new TypeAligned[Conversion[A, B] :: T] {
      type I = A
      type O = C
      def apply(l: Conversion[A, B] :: T): Conversion[A, C] =
        new Conversion[A, C] {
          def convert(a: A): C = tta(l.tail).convert(l.head.convert(a))
        }
    }
}

現在,您可以編寫一個AutoConversion版本,跟蹤有關管道的所有類型信息:

class AutoConversion[L <: HList, A <: Convertable, B <: Convertable](
  path: L
)(implicit ta: TypeAligned.Aux[L, A, B]) extends Conversion[A, B] {
  def convert(a: A): B = ta(path).convert(a)
}

你可以像這樣使用它:

case class AutoA(i: Int) extends Convertable
case class AutoB(s: String) extends Convertable
case class AutoC(c: Char) extends Convertable

val ab: Conversion[AutoA, AutoB] = new Conversion[AutoA, AutoB] {
  def convert(a: AutoA): AutoB = AutoB(a.i.toString)
}

val bc: Conversion[AutoB, AutoC] = new Conversion[AutoB, AutoC] {
  def convert(b: AutoB): AutoC = AutoC(b.s.lift(3).getOrElse('-'))
}

val conv = new AutoConversion(ab :: bc :: HNil)

並且conv將具有預期的靜態類型(並實現Conversion[AutoA, AutoC] )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM