1

我特別試圖定義半羣和'是'半羣的和類型,並通常使用ScalaCheck來檢查半羣的關聯屬性。爲在ScalaCheck中保存一個`Numeric`的case類創建一個任意實例?

我第一次在Haskell中寫過這個,因爲我發現先用Haskell語法先思考這些東西,然後再將它們轉換爲Scala,這很容易。

所以在Haskell,我寫這GHCI工作如下:

newtype Sum a = Sum a deriving (Show, Eq) 

instance Num a => Num (Sum a) where 
    (+) (Sum x) (Sum y) = Sum (x + y) 

class Semigroup a where 
    (<>) :: a -> a -> a 

instance Num a => Semigroup (Sum a) where 
    (<>) = (+) 

instance Arbitrary a => Arbitrary (Sum a) where 
    arbitrary = fmap Sum arbitrary 

semigroupAssocProp x y z = (x <> (y <> z)) == ((x <> y) <> z) 
quickCheck (semigroupAssocProp :: Num a => Sum a -> Sum a -> Sum a -> Bool) 

我試圖創造一些斯卡拉大致相當。到目前爲止,我中有你看看下面:

trait Semigroup[A] { 
    def |+|(b: A): A 
} 

case class Sum[A: Numeric](n: A) extends Semigroup[Sum[A]] { 
    def |+|(x: Sum[A]): Sum[A] = Sum[A](implicitly[Numeric[A]].plus(n, x.n) 
} 

val semigroupAssocProp = Prop.forAll { (x: Sum[Int], y: Sum[Int], z: Sum[Int]) => 
    (x |+| (y |+| z)) == ((x |+| y) |+| z) 
} 

val chooseSum = for { n <- Gen.chooseNum(-10000, 10000) } yield Sum(n) 
// => val chooseSum Gen[Sum[Int]] = org.scalacheck.Gen$$anon$<some hash> 

我失去了對如何創建一個更通用Sum[Numeric]一個Arbitrary實例,或至少Gen[Sum[Numeric]]以及如何創建一個更通用的semigroupAssocProp,可以採取一個類型爲S的x,y和z,其中S extends Semigroup[T],其中T是任何具體類型。

我真的想在功能上儘可能接近我在Scala中編寫的Haskell版本。問題的

回答

4

部分原因是,這是你的Haskell代碼更直接的翻譯:

trait Semigroup[A] { 
    def add(a: A, b: A): A 
} 

case class Sum[A](n: A) 

object Sum { 
    implicit def sumSemigroup[A: Numeric]: Semigroup[Sum[A]] = 
    new Semigroup[Sum[A]] { 
     def add(a: Sum[A], b: Sum[A]): Sum[A] = 
     Sum(implicitly[Numeric[A]].plus(a.n, b.n)) 
    } 
} 

這不是一個字面翻譯,因爲我們沒有爲Sum[A]供應Numeric實例(這將是更的疼痛,給出了Numeric的界面),但它確實代表了Scala中類型類的標準編碼。

現在你在完全相同的方式爲Sum[A]提供Arbitrary實例作爲哈斯克爾:

import org.scalacheck.Arbitrary 

implicit def arbitrarySum[A](implicit A: Arbitrary[A]): Arbitrary[Sum[A]] = 
    Arbitrary(A.arbitrary.map(Sum(_))) 

然後你可以定義你的財產:

import org.scalacheck.Prop 

def semigroupAssocProp[A: Arbitrary: Semigroup]: Prop = 
    Prop.forAll { (x: A, y: A, z: A) => 
    val semigroup = implicitly[Semigroup[A]] 

    semigroup.add(x, semigroup.add(y, z)) == semigroup.add(semigroup.add(x, y), z) 
    } 

然後檢查:

scala> semigroupAssocProp[Sum[Int]].check 
+ OK, passed 100 tests. 

關鍵是Scala不編碼類型類es使用子類型來實現你的實現 - 相反,你將類型類定義爲與你在Haskell中使用class非常相似的特徵(或類)。例如,我的Semigroup|+|有兩個參數,就像Haskell Semigroup中的<>一樣。儘管如此,您可以通過實例化這些特徵(或類)並將這些實例置於隱式範圍中來定義類型類實例,而不是單獨的類似於instance的機制。

+0

當我嘗試在Scala REPL中運行代碼時,出現聲明Sum類型不帶參數的錯誤。 def sum(a:Sum [A],b:Sum [A]):Sum [A] = Sum(隱含地[Numeric [A]]。加上(an,bn))' – josiah

+2

@josiah'Sum' case類和對象是一對伴侶,必須一起定義,如果您只是複製並粘貼到REPL中,則不會發生這種情況,但您可以使用' :粘貼'來一起定義它們。 –

+0

您的Scala版本確實看起來像'instance Num a => Semigroup(Sum a)其中'比問題中的版本更多。 – pedrofurla

相關問題