2017-03-03 7 views
5

Ich bin ziemlich neu in Scalacheck (und Scala ganz) so kann dies eine ziemlich einfache Lösung seinScalacheck Shrink

Ich bin mit einem Scalacheck AST erzeugen Tests für und überprüfen, ob der Schreiber/Parser Arbeit. Ich habe diese Dateien

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) 
    } 

} 

Im Beispiel Code, den ich geschrieben (was oben eingefügt wird) erhalte ich die Fehler

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) 

Wie schreibe ich die Schrumpfverfahren dafür?

Antwort

4

Sie haben zwei implizite Instanzen von Shrink[AST] und so beschwert sich der Compiler über mehrdeutige implizite Werte.

Sie könnten neu schreiben Ihren Code wie:

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

Dies ist genau das, was ich suche. Ich schätze als Nebenfrage, da ich drei verschiedene Dateien habe, eine für AST, Operation und Literal, wie bekomme ich Scala, um die impliziten Defs zu erkennen? –

+0

Sie könnten "Literal" - und "Operation" -Implikits (sowohl "Gen" als auch "Shrink") in zwei verschiedene Merkmale aufteilen. Dann können Sie sie zu einem einzigen 'AST'-Merkmal zusammenfassen und darin ein 'Shrink [AST]' definieren. Mischen Sie diese Merkmale in 'Properties'-Objekten, wenn sie benötigt werden. –

+0

Eine alternative Lösung könnte sein, sich nicht auf implicits zu verlassen und 'shrinkAST' explizit mit' shrinkLiteral (l) 'und' shrinkOperation (o) 'zu definieren. –

Verwandte Themen