2016-11-30 6 views
0

У меня есть свободная монады работа, которая делает то, что я хочу:Scala: Как составить список операций в бесплатную монаду?

type FreeOperation[F] = Free[Operation, F] 

sealed trait Operation[O] 
case object IdentityOperation extends Operation[GraphTraversal[_, _]] 
case class LabelOperation(label: String) extends Operation[GraphTraversal[_, Vertex]] 
case class HasOperation(has: String, within: List[_]) extends Operation[GraphTraversal[_, Vertex]] 
case class InOperation(in: String) extends Operation[GraphTraversal[_, Vertex]] 
case class OutOperation(out: String) extends Operation[GraphTraversal[_, Vertex]] 
case class InEdgeOperation(inEdge: String) extends Operation[GraphTraversal[_, Edge]] 
case class OutEdgeOperation(outEdge: String) extends Operation[GraphTraversal[_, Edge]] 
case class InVertexOperation(inVertex: String) extends Operation[GraphTraversal[_, Vertex]] 
case class OutVertexOperation(outVertex: String) extends Operation[GraphTraversal[_, Vertex]] 
case class AsOperation(as: String) extends Operation[GraphTraversal[_, _]] 
case class SelectOperation(select: List[String]) extends Operation[GraphTraversal[_, _]] 

object Operation { 
    def identity: FreeOperation[GraphTraversal[_, _]] = Free.liftF(IdentityOperation) 
    def label(v: String): FreeOperation[GraphTraversal[_, Vertex]] = Free.liftF(LabelOperation(v)) 
    def has(h: String, w: List[_]): FreeOperation[GraphTraversal[_, Vertex]] = Free.liftF(HasOperation(h, w)) 
    def in(i: String): FreeOperation[GraphTraversal[_, Vertex]] = Free.liftF(InOperation(i)) 
    def out(o: String): FreeOperation[GraphTraversal[_, Vertex]] = Free.liftF(OutOperation(o)) 
    def inEdge(ie: String): FreeOperation[GraphTraversal[_, Edge]] = Free.liftF(InEdgeOperation(ie)) 
    def outEdge(oe: String): FreeOperation[GraphTraversal[_, Edge]] = Free.liftF(OutEdgeOperation(oe)) 
    def inVertex(iv: String): FreeOperation[GraphTraversal[_, Vertex]] = Free.liftF(InVertexOperation(iv)) 
    def outVertex(ov: String): FreeOperation[GraphTraversal[_, Vertex]] = Free.liftF(OutVertexOperation(ov)) 
    def as(a: String): FreeOperation[GraphTraversal[_, _]] = Free.liftF(AsOperation(a)) 
    def select(s: List[String]): FreeOperation[GraphTraversal[_, _]] = Free.liftF(SelectOperation(s)) 
} 

def operationInterpreter(traversal: GraphTraversal[_, _]): (Operation ~> Id) = 
    new (Operation ~> Id) { 
    def apply[A](input: Operation[A]): Id[A] = 
     input match { 
     case IdentityOperation => traversal.asInstanceOf[A] 
     case LabelOperation(label) => traversal.hasLabel(label).asInstanceOf[A] 
     case HasOperation(has, within) => traversal.has(has, P.within(within: _*)).asInstanceOf[A] 
     case InOperation(in) => traversal.in(in).asInstanceOf[A] 
     case OutOperation(out) => traversal.out(out).asInstanceOf[A] 
     case InEdgeOperation(inEdge) => traversal.inE(inEdge).asInstanceOf[A] 
     case OutEdgeOperation(outEdge) => traversal.outE(outEdge).asInstanceOf[A] 
     case InVertexOperation(inVertex) => traversal.inV().asInstanceOf[A] 
     case OutVertexOperation(outVertex) => traversal.outV().asInstanceOf[A] 
     case AsOperation(as) => traversal.as(as).asInstanceOf[A] 
     case SelectOperation(select) => { 
      if (select.isEmpty) { 
      traversal 
      } else if (select.size == 1) { 
      traversal.select[Any](select.head).asInstanceOf[A] 
      } else { 
      traversal.select[Any](select.head, select.tail.head, select.tail.tail: _*) 
      } 
     } 
     } 
    } 

Я могу назвать это как так, чтобы создать программу:

def selectQuery: Free[Operation, GraphTraversal[_, _]] = 
    for { 
    _ <- label("person") 
    _ <- as("people") 
    _ <- outEdge("created") 
    _ <- has("weight", List(1.0)) 
    _ <- inVertex("software") 
    _ <- as("software") 
    x <- select(List("people", "software")) 
    } yield x 

val traversal = graph.traversal.V() 
val result = selectQuery.foldMap(operationInterpreter(traversal)) 

Круто! Но вот в чем проблема:

У меня есть List[Operation], что я хотел бы перевести на эту монадическую структуру ... как это сделать?

def composeQuery(query: List[Operation[_]]): FreeOperation[GraphTraversal[_, _]] = { 
    query.foldLeft(???) (??????) 
} 

Что такое правильный способ перевести мой List из Operations в свободную монаду, что я могу передать мой переводчик?

+0

Не могли бы вы отобразить 'Free.liftF' над коллекцией, создать список монадов, а затем использовать монадическую функцию' sequence', чтобы последовательно составлять монады? –

+0

@ jon-hanson Это было неудачно для меня, но я заменил его 'sequenceU', и он сработал! По-видимому, для этого существует проблема scala: https://github.com/milessabin/si2712fix-plugin – prismofeverything

+0

Да, 'sequenceU' - это вариант Unapply' sequence', необходимый для работы [SI-2712] (https://issues.scala-lang.org/browse/SI-2712) - больше информации [здесь] (http://eed3si9n.com/herding-cats/Unapply.html). –

ответ

1

Карта Free.liftF над коллекцией, чтобы создать список монадов, а затем использовать монадическую функцию sequence (на самом деле sequenceU, как вы указываете), чтобы последовательно составлять монады.