Scala

2011-09-08 5 views
9

'da karşılıklı bağımlı varsayılan yöntem uygulamalarının çıkarılması İyi tanımlanmış bir ilişkiye sahip bazı özelliklere sahip bir özellik tanımlamak istiyorum - örneğin, a * b = c. Bu özellik, bu özelliğin uygulamalarının bunlardan ikisini sağlayabilmesi ve otomatik olarak türetilen üçüncü özellik için bir erişim sahibinin bulunmasıdır. Eğer geri kalan sürece fonksiyonların herhangi bir alt kümesini tanımlayabiliriz olsa -Scala

( Bu Haskell'ın tip sınıfları aynı özelliği Doğru, sen Ord den < tanımlı eğer bilinmeyenler hatırlarsanız, >=! . < olarak uygulanacağına dair olduğu . anlaşılmaktadır olabilir) (Ben doğru Haskell'ın tip sınıfları hatırlamıyorum)

naif yaklaşım aslında oldukça iyi çalışıyor.

İşte
trait Foo { 
    // a * b = c 
    def a: Double = c/b 
    def b: Double = c/a 
    def c: Double = a * b 
} 

class FooOne(override val a: Double, override val b: Double) extends Foo 
class FooTwo(override val a: Double, override val c: Double) extends Foo 

, uygulamaları FooOne ve FooTwo, Foo'un tam uygulamalarıdır ve beklendiği gibi davranır. Çok uzak çok iyi; Bu yaklaşım, sınıfların özelliklerden iki tanesini tanımlamasına ve üçüncüsünü "ücretsiz" almasına izin verir.

Ancak, işler bir üçüncü sınıf tanımlıyorsa az pembe bakmaya başlar:

class FooFail(override val a: Double) extends Foo 

Bu gayet derler - onun b veya c yöntemleri hiç değerlendirilirse ancak, bir yığın taşmasına neden olacaktır.


Yani naif yaklaşım Haskell'ın tip sınıf yaklaşımının çıkarsama yönünü verir, ama biz derleme zamanı güvenliği yok. İstediğim şey, derleyicinin, yöntemlerin ikiden azının, sınıfları uygulayarak tanımlanması durumunda şikayet etmesidir. Açıkçası mevcut sözdizimi burada yeterli değil; soyut ve düşünülen metotlara, eğer sadece bağımlı yöntemler soyut değilse, kullanılabilecek bir varsayılan uygulama ile olsa gerek.

Scala bunu tanımlamak için uygun semantik gösteriyor mu? (Bu dil için birinci sınıf bir desteğin farkında olmadığım için, union types'a benzer, onu tanımlamanın biraz dolambaçlı bir yolu varsa, bir sorunum yok). Aksi takdirde, saf olmayan yaklaşımla başa çıkacağım ve sadece sınıflarımı dikkatlice tanımlayıp test edeceğim. Ama bunun gerçekten de sistem tipinin yakalayabileceği bir şey olduğunu düşünüyorum (her şeyden önce - Ruby değil.).

+2

Haskell tamamen yanlış tanımlamadıkça, aynı sorunla tam olarak aynı saf yaklaşımı kullanır. –

+0

@Alexey - ilginç yorum. Biraz zaman geçti, ama eğer metodları yeterince tanımlamamış olsaydınız, satırlar boyunca bir derleyici/yorumlayıcı hatası alacağınızı, sınıf için tüm yöntemleri türetmenin mümkün olmadığını hatırlıyorum. Belki bunu yeniden üretmeye çalışacağım ve ilham için Haskell'in nasıl yaptığını okuyacağım. –

+0

Tamam, bunu Haskell'de test ettikten sonra, gerçekten de aynı sorundan muzdariptir (Scala durumunun sadece tamamen soyut yöntemler hakkında uyarmasıyla eşdeğerdir). Bununla birlikte, soru başka bir dilde emsali olmadan da olsa hala geçerli. –

cevap

8

Implicits kullanın:

+0

Çok ilginç bir yaklaşım - didierd'e yaptığım yorumlar uyarınca, örtücülüğün yeterli tanımların mevcut olup olmadığını kontrol etmesinde önemli bir etken olacağından kuşkuluyum. Tüketicilerin artık "val b = 5" değerini artık bildiremeyeceği bir utançtır. Bu, başka bir iç özellik katmanı aracılığıyla masaj yapılıp yapılamayacağını merak ediyorum. –

5

Haskell'de güvenliği nasıl kazanıyorsunuz? Dile çok aşina değilim, ancak

ve karşılaştırarak değerlendirirken bir yığın taşması alabilirsiniz.

Scala'da bir geçici çözüm hayal edebiliyorum ama oldukça zayıf bir tane. İlk olarak, Sonra

trait FooWithAB extends Foo {def c : Double = a * b} 
trait FooWithAC extends Foo {def b : Double = c/a} 
trait FooWithBC extends Foo {def a : Double = c/b} 

FooOne ve FooTwo özelliğin birinde karıştırmak zorunda kalacak izin her yöntemin tanımı kombinasyonu için mixin özellikleri oluşturmak

trait Foo { def a: Double; def b: Double; def c: Double } 

tamamen soyut Foo bırakın.FooFail'de hiçbir şeyden iki tanesini karıştırmazsınız ve hala başarısız olursunuz, ama bir şekilde uyardınız. , Tanımladığınız edemez

Is

trait Foo { 
    type t; 
    def a: Double; def b: Double; def c: Double 
} 
trait FooWithAB extends Foo {type t = FooWithAB; def c : Double = a * b} 
trait FooWithAC extends Foo {type t = FooWithAC; def b : Double = c/a} 
trait FooWithBC extends Foo {type t = FooWithBC; def c : Double = c/b} 

Bu FooWithXX ikisini karıştırma önlemek fantom tip bir tür, bir adım daha ileri gidip ikisini karıştırma yasaklamak mümkündür

class FooFail(val a: Double) extends FooWithAC with FooWithAB 
+0

İyi bir nokta - Herhangi bir çözümün Foo'da tamamen soyut bir şekilde soyut bırakılmasını ve sonra * bir yerden * varsayılan tanımları karıştırmayı içereceğini sanıyorum. Fantom tipi yaklaşım ilginçtir; ancak, becerinin açık bir şekilde özel olarak karışması gereken tüketicilere maruz kalması bir utançtır. Belki de örtük dönüşümler bu şekilde işe yarayabilir, hmm ... –

2

Bir çözüm biraz daha zayıf bir çözüm Eğer takip ediyor isteyebilirsiniz:

trait Foo { 
    def a : Double 
    def b : Double 
    def c : Double 
} 

// Anything with two out of three can be promoted to Foo status. 
implicit def ab2Foo(ab : { def a : Double; def b : Double }) = 
    new Foo { val a = ab.a; val b = ab.b; def c = ab.a * ab.b } 
implicit def bc2Foo(bc : { def b : Double; def c : Double }) = 
    new Foo { val a = bc.c/bc.b; val b = bc.b; def c = bc.c } 
implicit def ac2Foo(ac : { def a : Double; def c : Double }) = 
    new Foo { val a = ac.a; val b = ac.c/ac.a; def c = ac.c } 

Burada üçe bir ikisiyle herhangi sınıfı, b, c yöntemleri inceledi ve bir Foo olarak kullanılabilir. Örneğin:

case class AB(a : Double, b : Double) 
AB(5.0, 7.1).c // prints 35.5 

Ama mesela denerseniz:

case class ABC(a : Double, b : Double, c : Double) 
val x : Foo = ABC(1.0, 2.0, 3.0) 

... "belirsiz örtülü" hatası alıyorum.

Asıl sorun, esas ifadeniz, sınıfların Foo'dan uygun şekilde devralmadığıdır; bu, başka yöntemleri yeniden kullanmak isterseniz sorun olabilir. Yine de, bu diğer yöntemleri içeren başka bir FooImpl özelliğine sahip olarak çalışarak ve gizli dönüşümleri FooImpl alt türleriyle sınırlandırabilirsiniz.

+0

Burada yaptığınız her şeyi, tüketiciye herhangi bir ek gereksinim duymadan, derleme zamanında kontrolü gerçekleştirmek için örtülü dönüşümlerle birlikte yapısal türleri kullanarak seviyorum. “Foo” olmanın ideal olmadığına katılıyorum, ancak diğer gerekliliklerin ikisini birden elde etmek için kaçınılmaz olabileceğinden korkuyorum (“Foo” daki yöntemler aynı anda hem soyut hem de tanımlanmış olmalıdır). –