2011-02-15 15 views
6

Ben Haskell basit ışın-izleyici yazmaya çalışıyorum. Bir ışın bunları kesiştiği belirlemek için bir fonksiyonu olan, mevcut yüzeylerin çeşitli temsil eden bir typeclass tanımlamak istediği:Kısıtlayıcı tipte değişken 'blah' kısıtlaması ... nasıl düzeltilir?

Ambiguous type variable 's' in the constraint: 
    'Surface' 
    arising from a use of 'surface' 

(gerçek:

{-# LANGUAGE RankNTypes #-} 

data Vector = Vector Double Double Double 
data Ray = Ray Vector Vector 

class Surface s where 
    intersections :: s -> Ray -> [Vector] 

-- Obviously there would be some concrete surface implementations here... 

data Renderable = Renderable 
    { surface :: (Surface s) => s 
    , otherStuff :: Int 
    } 

getRenderableIntersections :: Renderable -> Ray -> [Vector] 
getRenderableIntersections re ra = intersections (surface re) ra 

Bununla birlikte, bu bana hata verir Kod daha karmaşıktır ama elde etmeye çalıştığım şeyi korurken, daha basit bir şeye damlatmaya çalıştım).

Bunu nasıl düzeltebilirim? Ya da alternatif olarak, standart bir OO arka planından geldiğim için, temel olarak neyi yanlış yapıyorum? senin getRenderableIntersections fonksiyonda

cevap

10

Lütfen bunun için varolan türleri kullanmayın! Yapabilirsin, ama bir anlamı olmazdı.

fonksiyonel açıdan tamamen Yüzey bu typeclass kavramını bırakabilirsiniz.Bir Surface, bir Ray'ı Vektörler listesine eşleyen bir şeydir, değil mi? Yani:

type Surface = Ray -> [Vector] 

data Renderable = Renderable 
{ surface :: Surface 
, otherStuff :: Int 
} 

Şimdi gerçekten istiyorsanız, size verdi aslında bir ToSurface typeclass sahip olabilir:

class ToSurface a where 
    toSurface :: a -> Surface 

Ama bu sadece kolaylık ve ad-hoc polimorfizmi için. Modelinizde hiçbir şey gerektirmez. Genel olarak, varoluşçular için çok az sayıda kullanım vakası vardır, fakat zamanın en az% 90'ı, varoluşunu temsil ettiği işlevlerle değiştirebilir ve akılda kalıcı ve daha akılda kalıcı bir şey elde edebilirsiniz. Ayrıca

, içeri almak ve sorunları tam olarak uyuşmuyor için çok fazla bir tad olsa bile, sen denotasyonel tasarımı Conal en yazma bazı yararlı bulabilirsiniz: ... http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics/

3

sen surface diyoruz. Tercümanın kullanmak istediğiniz sınıfın hangi örneğinin Surface olduğunu anlaması mümkün değildir. İki tür örneklerini varsa:

instance Surface SurfaceA where 
    -- ... 
instance Surface SurfaceB where 
    -- ... 

Nasıl tercüman surface türünü belirleyebilir?

Eğer Renderable tanımlı yolu bir işlev surface :: Surface s => Renderable -> s olduğu anlamına gelir.

> :t surface (Renderable SurfaceA 0) -- What's the type of the expression? 

Yani, bu ifadenin ne tür:

(basit bir yapıcısı SurfaceA verilen) şu tip sorgu örneği Surface SurfaceA oluşturup soran deneyin? Bahse girerim SurfaceA'u bekliyordur. Yanlış. surface türünü alın. Bu bir Renderable argüman alır ve biz bunu Renderable argüman geçiyoruz. Bundan sonra ne kaldı? Surface s => s. Bu o ifadenin türü. Hala ne tür s temsil ettiğini bilmiyoruz.

Türün SurfaceA olmasını istiyorsanız, kodunuzu değiştirmeniz gerekir, böylece surface :: Surface s => Renderable s -> s gibi bir şey olur. o Renderable kullanılan aynı s çünkü ne s Bu sayede belirlenebilir.

DÜZENLEME: @mokus önerdiği gibi, aynı zamanda ExistentialTypes uzantısı deneyebilirsin. Bu bir tür bildirimi sağ tarafındaki deplasmanda tür parametreleri "gizleme" verir.

data Renderable = forall s. Surface s => Renderable 
    { surface :: s 
    , otherStuff :: Int 
    } 

Hatta sen ne yapmak istediğini çok benzer an example sahiptir Yukarıda bağlantılı HaskellWiki sayfa.

DÜZENLEME: (By @stusmith) - Kayıt için, aşağıda bu önerilere dayanan derleme kodları ekliyorum. Ancak, daha iyi bir şeylere yaklaştığını düşündüğüm cevabı kabul ettim.

{-# LANGUAGE ExistentialQuantification #-} 

data Vector = Vector Double Double Double 
data Ray = Ray Vector Vector 

class Surface_ s where 
    intersections :: s -> Ray -> [Vector] 

data Surface = forall s. Surface_ s => Surface s 

instance Surface_ Surface where 
    intersections (Surface s) ra = intersections s ra 

data Renderable = Renderable 
    { surface :: Surface 
    } 

getRenderableIntersections :: Renderable -> Ray -> [Vector] 
getRenderableIntersections re ra = intersections (surface re) ra 
+0

ama tüm yüzeyler 'kesişme' yöntemini uyguladığı için, neden bu yeterli değil? Açıkçası arka planım ile 'Surface'i soyut bir sınıf olarak görüyorum ve sanal bir yöntem olarak' kavşakları 'görüyorum. – stusmith

+0

@stusmith: Güncellemeye bakın. İfadelerin ortasında açık tip parametreleri olamaz. Tüm ifadenin parametrik olması ya da türlerin tümü statik olarak belirlenebilir. (Belki bir GHC uzantısı ya da buna izin veren bir şey var, ama ben bunu bilmiyorum) –

+0

Bu, Haskell'in 'sanal' işlevlere eşdeğer olmadığı anlamına mı geliyor? Renderables'ın bir listesini yapmak ve soyut tipler üzerinde çalışabilmek istedim. Bu nedenle, bunun yerine [Yenilenebilir s] 'ye ihtiyacım var mı? Ve bu liste farklı "Yüzey" örnekleriyle heterojen "Değiştirilebilir" örnekleri içerebilir mi? – stusmith

İlgili konular