2017-03-03 32 views
5

我是相當新的ScalaCheck(和Scala完全),所以這可能是一個相當簡單的解決方案Scalacheck收縮

我使用ScalaCheck產生用於AST測試和驗證作家/解析器的工作。我有這些文件

AST.scala

package com.test 

object Operator extends Enumeration { 
    val Add, Subtract, Multiply, Divide = Value 
} 

sealed trait AST 
case class Operation(left: AST, op: Operator.Value, right: AST) extends AST 
case class Literal(value: Int) extends AST 

GenOperation.scala

import com.test.{AST, Literal} 

import org.scalacheck._ 
import Shrink._ 
import Prop._ 
import Arbitrary.arbitrary  

object GenLiteral extends Properties("AST::Literal") { 
    property("Verify parse/write") = forAll(genLiteral){ (node) => 
    // val string_version = node.writeToString() // AST -> String 
    // val result = Parse(string_version) // String -> AST 
    true 
    } 

    def genLiteral: Gen[Literal] = for { 
    value <- arbitrary[Int] 
    } yield Literal(value) 

    implicit def shrinkLiteral: Shrink[AST] = Shrink { 
    case Literal(value) => 
     for { 
     reduced <- shrink(value) 
     } yield Literal(reduced) 
    } 
} 

GenOperation.scala

import com.test.{AST, Operation} 

import org.scalacheck._ 
import Gen._ 
import Shrink._ 
import Prop._ 

import GenLiteral._ 

object GenOperation extends Properties("AST::Operation") { 
    property("Verify parse/write") = forAll(genOperation){ (node) => 
    // val string_version = node.writeToString() // AST -> String 
    // val result = Parse(string_version) // String -> AST 
    true 
    } 

    def genOperation: Gen[Operation] = for { 
    left <- oneOf(genOperation, genLiteral) 
    right <- oneOf(genOperation, genLiteral) 
    op <- oneOf(Operator.values.toSeq) 
    } yield Operation(left,op,right) 

    implicit def shrinkOperation: Shrink[AST] = Shrink { 
    case Operation(l,o,r) => 
     (
     for { 
      ls <- shrink(l) 
      rs <- shrink(r) 
     } yield Operation(ls, o, rs) 
     ) append (
     for { 
      ls <- shrink(l) 
     } yield Operation(ls, o, r) 
     ) append (
     for { 
      rs <- shrink(r) 
     } yield Operation(l, o, rs) 
     ) append shrink(l) append shrink(r) 
    } 

} 

在示例代碼我寫的(什麼是上面粘貼)我得到的錯誤

ambiguous implicit values: 
both method shrinkLiteral in object GenLiteral of type => org.scalacheck.Shrink[com.test.AST] 
and method shrinkOperation in object GenOperation of type => org.scalacheck.Shrink[com.test.AST] 
match expected type org.scalacheck.Shrink[com.test.AST] 
      ls <- shrink(l) 

我如何寫這個收縮的方法呢?

回答

4

您有兩個隱含的Shrink[AST]實例,因此編譯器會抱怨模糊的隱式值。

你可以重新編寫代碼爲:

implicit def shrinkLiteral: Shrink[Literal] = Shrink { 
    case Literal(value) => shrink(value).map(Literal) 
} 

implicit def shrinkOperation: Shrink[Operation] = Shrink { 
    case Operation(l,o,r) => 
    shrink(l).map(Operation(_, o, r)) append 
    shrink(r).map(Operation(l, o, _)) append ??? 
} 

implicit def shrinkAST: Shrink[AST] = Shrink { 
    case o: Operation => shrink(o) 
    case l: Literal => shrink(l) 
} 
+0

這正是我所期待的。我猜是一個側面問題,因爲我有三個不同的文件,分別是AST,Operation和Literal,我如何讓Scala識別隱式def? –

+0

你可以將'Literal'和'Operation'含義('Gen'和'Shrink')分成兩個不同的特徵。然後你可以將它們混合成一個'AST'特性並在其中定義一個'收縮[AST]'。需要時,將這些特徵混合在「屬性」對象中。 –

+0

另一種解決方案可能不是依賴implicits並使用'shrinkLiteral(l)'和'shrinkOperation(o)'顯式地定義'shrinkAST'。 –