2015-10-06 1 views
2

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

trait Chain[A]{ 
    type ChainMethod = A => A //type of the method chained so far 

    def flatMap[B](f: A => Chain[B]): Chain[B] //the ChainMethod needs to be included in the return type somehow 
    def map[B](f: A => B): Chain[B]: Chain[B] 

    def fill: ChainMethod //Function has to be uncurried here 
} 

В качестве примера несколько конкретных типов Chain:

object StringChain extends Chain[String] 
object IntChain extends Chain[Int] 

и класс случая, который будет использоваться:

case class User(name:String, age:Int) 

цепи может быть создано с пониманием:

val form = for{ 
    name <- StringChain 
    age <- IntChain 
} yield User(name, age) 

типа form должен быть

Chain[User]{type ChainMethod = String => Int => User} 

так, что мы можем сделать следующее:

form.fill("John", 25) //should return User("John", 25) 

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

ответ

1

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

Это становится проще, если вы используете библиотеку shapeless, предназначенную для вычислений на уровне уровня. В следующем коде используется несколько иной подход, где Chain.fill - это функция из кортежа аргументов в результат. Эта реализация flatMap также позволяет объединить несколько форм в одну:

import shapeless._ 
import shapeless.ops.{tuple => tp} 

object Chain { 
    def of[T]: Chain[Tuple1[T], T] = new Chain[Tuple1[T], T] { 
    def fill(a: Tuple1[T]) = a._1 
    } 
} 

/** @tparam A Tuple of arguments for `fill` 
    * @tparam O Result of `fill` 
    */ 
abstract class Chain[A, O] { self => 
    def fill(a: A): O 

    def flatMap[A2, O2, Len <: Nat, R](next: O => Chain[A2, O2])(
    implicit 
     // Append tuple A2 to tuple A to get a single tuple R 
     prepend: tp.Prepend.Aux[A, A2, R], 
     // Compute length Len of tuple A 
     length: tp.Length.Aux[A, Len], 
     // Take the first Len elements of tuple R, 
     // and assert that they are equivalent to A 
     take: tp.Take.Aux[R, Len, A], 
     // Drop the first Len elements of tuple R, 
     // and assert that the rest are equivalent to A2 
     drop: tp.Drop.Aux[R, Len, A2] 
): Chain[R, O2] = new Chain[R, O2] { 
    def fill(r: R): O2 = next(self.fill(take(r))).fill(drop(r)) 
    } 

    def map[O2](f: O => O2): Chain[A, O2] = new Chain[A, O2] { 
    def fill(a: A): O2 = f(self.fill(a)) 
    } 
} 

А вот как вы можете использовать его:

scala> case class Address(country: String, city: String) 
defined class Address 

scala> case class User(id: Int, name: String, address: Address) 
defined class User 

scala> val addressForm = for { 
    country <- Chain.of[String] 
    city <- Chain.of[String] 
} yield Address(country, city) 
addressForm: com.Main.Chain[this.Out,Address] = [email protected] 

scala> val userForm = for { 
    id <- Chain.of[Int] 
    name <- Chain.of[String] 
    address <- addressForm 
} yield User(id, name, address) 
userForm: com.Main.Chain[this.Out,User] = [email protected] 

scala> userForm.fill(1, "John", "USA", "New York") 
res0: User = User(1,John,Address(USA,New York)) 
Смежные вопросы