2016-09-14 6 views
3

Я работаю над API, который должен позволить динамически строить бесформенную функцию Poly1 из стандартных мономорфных функций, которые работают с типами некоторых копроизведений.Полифункция из мономорфных функций, работающих на копродукте

Цель состоит в том, чтобы выставить простой метод, который принимает функцию:

type FooCoproduct = Foo :+: Bar :+: CNil 
def addF[E](f: E => E)(implicit ev: Inject[FooCoproduct, E]) = ??? 

и накапливать эти функции, чтобы построить общую функцию Poly1, охватывающую все типы в копроизведении. Свидетельство ev здесь заключается в том, что тип пареметра E является типом копроизведения.

После тестирования нескольких подходов, в том числе родового деривации типов, наиболее перспективный способ привел меня к накоплению этих мономорфных функций в HList и попытаться решить тот, который применяется с помощью Selector. Это, вероятно, лучше понять, например:

object CoproductSample extends App { 

    import shapeless.{ :+:, CNil, Coproduct, HList, HNil, Poly1 } 
    import shapeless.ops.coproduct.Inject 
    import shapeless.ops.hlist.Selector 

    class Builder[A <: Coproduct] { 

    def accum[B](f: B => B, hl: HList)(implicit ev: Inject[A, B]) = f :: hl 

    class PolyBuilder[L <: HList](hl: L) extends Poly1 { 
     implicit def run[T](implicit ev: Selector[L, T => T]) = 
     at[T](hl.select[T => T]) 
    } 

    } 

    type Cop = Int :+: String :+: CNil 

    val builder = new Builder[Cop] 

    val hl1 = builder.accum((i: Int) => i + 1, HNil) 
    val hl2 = builder.accum((s: String) => s + "one", hl1) 

    object pf extends builder.PolyBuilder(hl2) 

    val rInt = Coproduct[Cop](10).fold(pf) 
    val rStr = Coproduct[Cop]("ten").fold(pf) 
} 

Этот код не компилировать с сообщением:

could not find implicit value for parameter folder:  
shapeless.ops.coproduct.Folder[CoproductSample.pf.type, CoproductSample.Cop] 

Я полагаю, что мне нужно, чтобы обеспечить Selector[L, T => T] где L является тип накопленная HList но я не могу придумать, как это сделать. С другой стороны, я чувствую, что должно быть более простое решение моей проблемы.

Любая помощь будет оценена по достоинству.

Update

После этого еще несколько исследований я пришел к решению, что почти работает. К сожалению, я не могу правильно отслеживать тип результата.

object CoproductSample { 
    import shapeless.{ CNil, Coproduct, HList, HNil, Inl, Inr, Poly2, ::, :+: } 

    // Accumulates ordinary functions from A => A in an HList 
    def accum[A, L <: HList](f: A => A, hl: L): (A => A) :: L = f :: hl 

    // A poly2 function that evaluates some monomorphic function present in 
    // an HList for certain value that satifies the signature of this function 
    object PolyEval extends Poly2 { 
    implicit def hnilCase[A]: Case.Aux[A, HNil, Option[A]] = 
     at[A, HNil]((a, l) => None) 

    implicit def hheadCaseSuccess[A, T <: HList]: Case.Aux[A, (A => A) :: T, Option[A]] = 
     at[A, (A => A) :: T]((a: A, l: (A => A) :: T) => Option(l.head(a))) 

    implicit def hheadCaseFail[A, H, T <: HList](
     implicit tail: Case.Aux[A, T, Option[A]] 
    ): Case.Aux[A, (H => H) :: T, Option[A]] = 
     at[A, (H => H) :: T]((a: A, l: (H => H) :: T) => PolyEval(a, l.tail)) 
    } 

    // A poly2 function that uses `PolyEval` for evaluating a value present in 
    // a coproduct against an HList of monomorphic functions 
    object PolyEvalCop extends Poly2 { 
    implicit def cnilCase[A <: CNil, L <: HList]: Case.Aux[A, L, Option[A]] = 
     at[A, L]((a, l) => sys.error("Impossible!")) 

    implicit def cconsCase[H, T <: Coproduct, L <: HList](
     implicit head: PolyEval.Case.Aux[H, L, Option[H]], 
     tail: Case[T, L]) // What is the return type here???) 
    = at[H :+: T, L]((c, l) => 
     c match { 
      case Inl(h) => PolyEval(h, l) 
      case Inr(t) => PolyEvalCop(t, l) 
     }) 
    } 
} 

консоли сеанса:

scala> import shapeless._, CoproductSample._ 
import shapeless._ 
import CoproductSample._ 

scala> case class Foo(i: Int); case class Bar(s: String) 
defined class Foo 
defined class Bar 

scala> val f = (foo: Foo) => foo.copy(i = foo.i * 2) 
f: Foo => Foo = <function1> 

scala> val g = (bar: Bar) => bar.copy(s = bar.s + "_changed!") 
g: Bar => Bar = <function1> 

scala> val hl = accum(g, accum(f, HNil)) 
hl: shapeless.::[Bar => Bar,shapeless.::[Foo => Foo,shapeless.HNil.type]] = <function1> :: <function1> :: HNil 

scala> type C = Foo :+: Bar :+: CNil 
defined type alias C 

scala> PolyEvalCop(Coproduct[C](Foo(10)), hl) 
res1: Any = Some(Foo(20)) 

scala> PolyEvalCop(Coproduct[C](Bar("bar")), hl) 
res2: Any = Some(Bar(bar_changed!)) 

Тип результата не правильно отслеживается, и это решить, как Any.

+0

я мог бы попытаться взглянуть позже, но в то же время, я рекомендую вам скомпилировать его с «-Xlog- implicits " – caente

ответ

0

С подписью addF он выглядит, как вы хотите отобразить над Coproduct, то есть изменить его значение и оставаться на копроизведением, т.е. def add[E](e:E):E, если бы это было так, это будет работать:

@ { 
    trait Add[E]{ 
    def add(e:E):E 
    } 
    object Add{ 
    def apply[E:Add]:Add[E] = implicitly[Add[E]] 
    implicit object cnil extends Add[CNil] { 
    def add(e:CNil) = throw new RuntimeException("Impossible") 
    } 
    implicit def coproduct[H, T <: Coproduct](
       implicit 
       addH: Add[H], 
       addT:Add[T], 
       basis: ops.coproduct.Basis[H :+: T,T] 
       ):Add[H :+: T] = new Add[H :+: T]{ 
     def add(e: H :+: T) = e match { 
      case Inl(h) => Coproduct[H :+: T](addH.add(h)) // to stay in the Coproduct 
      case Inr(t) => addT.add(t).embed[H :+: T] // to stay in the coproduct 
     } 
     } 
    } 
    } 
defined trait Add 
defined object Add 
@ implicit def addString = new Add[String] { 
    def add(e:String) = e + "-ah" 
    } 
defined function addString 
@ implicit def addInt = new Add[Int] { 
    def add(e:Int) = e + 1 
    } 
defined function addInt 
@ type C = Int :+: String :+: CNil 
defined type C 
@ val i = Coproduct[C](1) 
i: C = 1 
@ Add[C].add(i) 
res24: C = 2 // notice that the return type is C 
@ val s = Coproduct[C]("a") 
s: C = a 
@ Add[C].add(s) 
res26: C = a-ah // notice that the return type is C 

Это очевидно, работает с «простыми» типами:

@ Add[Int].add(1) 
res38: Int = 2 

выше эквивалентен map; но если вы хотите fold, т.е. def add[E](e:E):Int, вы бы просто изменить эти две строки:

case Inl(h) => addH.add(h) 
case Inr(t) => addT.add(t) 
+0

Спасибо за ваш интересный ответ. Тем не менее, я намерен предоставить пользователю вспомогательный метод для добавления простых функций со своей собственной логикой без необходимости писать дополнительные типы имен для пользовательских алгебр. Я не вижу, как достичь этой цели с помощью подхода, основанного на стандартном стиле.Фактически, это похоже на одну из моих первых попыток, которые заставили меня попробовать с общим деривацией типов. –

+0

Пожалуйста, примите во внимание, что пользователи моего API будут работать со своими собственными типами, и я не хочу, чтобы они имели дело с бременем написания расширенного кода, но простые простые мономорфные функции. Вот почему я ищу решение, которое накапливает функции в HList, что позволит впоследствии сбросить значения копроизведения. –

+0

Я вижу, тем не менее, я думаю, что я помню, как читал мили, обескураживая полифункции, так как вы видите, что они в основном ad-hoc типа-класса ... вы скомпилировали ваш пример с помощью '-Xlog-implicits'? – caente

 Смежные вопросы

  • Нет связанных вопросов^_^