2013-04-25 2 views
13

Objektif paketi ile kodlama yapıyordum. Bir cebirsel türüne Belirli bir alanı erişmeye çalıştı kadar her şey iyi gidiyordu:Cebirsel tipli lens paketi

import Control.Lens 

data Type = A { _a :: Char } | B 

makeLenses ''Type 

test1 = _a (A 'a') 
test2 = (A 'a') ^. a 

No instance for (Data.Monoid.Monoid Char) 
    arising from a use of `a' 
Possible fix: 
    add an instance declaration for (Data.Monoid.Monoid Char) 
In the second argument of `(^.)', namely `a' 
In the expression: (A 'a') ^. a 
In an equation for `test2': test2 = (A 'a') ^. a 

Sadece _A ile gidebiliriz, ama benim gerçek programda veri türü çok daha derin ve ben tür Merceği kullanarak hedeflenen Yapmam gereken iş miktarını azalt. Objektif kütüphanesine baktım ama orada çok fazla var ve bu senaryo ile ilgilenip ilgilenmediğinden emin değilim ya da objektif kütüphanesinin desteklemediği bir şey.

Bir yan not olarak, aslında Char yerine veri türü için tekdüze bir String'i kullanırsam, daha sonra derler ve doğru yanıtı verir, neden olduğunu bilmiyorum.

Düzenleme: Hammar yorumuna okuduktan sonra bu denenmiş ve bu işler:

test2 = (A 'a') ^? a 
test3 = B ^? a 

Ama garip varolmaya olan bir şey için bunun dışında bir belki elde etmektir.

+5

Lens paketinin iç işleyişine aşina değil, ama: Ne olduğunu düşünün. a' dönmelidir. Bir şey seçmek zorundadır, bu yüzden 'memecy '' _a B' gibi bir istisna atmak yerine bir varsayılan olarak kullanmaya çalışır. – hammar

+2

_a B'nin derleyeceği hakkında hiçbir fikrim yoktu. Kodumda, çalışma zamanına kadar orada olduğunu anlayamayacağım bir hata vardı ve Duvar ile ilgili bir uyarı bile yoktu. Yani, sanırım^kullanacağım? ve belki de işlev ve tam olarak ihtiyacım olan şeyi yapar. –

+3

Aslında ikinci düşünceme göre, cebirsel tiplerdeki erişim metotlarının sadece kötü bir fikir olduğunun farkına vardım. Buna izin verildiğine şaşırdım. Programımda, sadece temizlediğim çok fazla hata vardı. –

cevap

5

Bunun yanıtlanabilmesi için, benim problemim, farklı kurucular arasında bazı alanların ortak olduğu bir cebirsel türüm vardı; ancak, kullanmaya çalışmadığım takdirde çalışma zamanında paylaşılamayan birkaç alan vardı. onlar.

data Exercise = 
    BarbellExercise { 
    name :: String, 
    weight :: Int, 
    reps :: Int 
    } | 
    BodyWeightExercise { 
    name :: String, 
    reps :: Int 
    } 

exer1 = BarbellExercise "Squats" 235 15 
exer2 = BarbellExercise "Deadlifts" 265 15 
exer3 = BodyWeightExercise "Pullups" 12 
exer4 = BarbellExercise "Overhead Press" 85 15 

workout = [exer1, exer2, exer3, exer4] 

test = do 
    mapM_ displayExercise workout 

    where 
    displayExercise x = putStrLn $ "Exercise: " ++ (name x) ++ " You must perform " ++ (show $ reps x) ++ "@" ++ (show $ weight x) 

Bu derler ama kilo fonksiyonunu kullanarak hata yaparsanız çalışma zamanını ölür. Anlaşılabilir hata. Lensler örnek oluşturmak için haskell şablonunu kullandığında, bunu fark eder ve bir hatayı önlemek için davranışını değiştirir. Alan erişimcileri kaldırabilirsiniz ancak benim durumumda, alanların çoğu veri tipleri arasında aynıydı. Burada alanlar eşleşmedi fark bir kez veri türünü yazdım nasıl açıklanmıştır:

data Exercise = 
    BarbellExercise 
    String --^name 
    Int --^reps 
    Int --^weight 
    | 
    BodyWeightExercise 
    String --^name 
    Int -- reps 


name :: Exercise -> String 
name (BarbellExercise n _ _) = n 
name (BodyWeightExercise n _) = n 

reps :: Exercise -> Int 
reps (BarbellExercise _ r _) = r 
reps (BodyWeightExercise _ r) = r 

bu şekilde yaparak, biraz daha az temiz iken, hata derleme sırasında yakalanır. Fonksiyonları kendim yazmam için beni zorlayarak, yazdığım gibi kısmi işlevleri görebiliyordum.

Ghc'nin beni uyarmasını isterdim. Böyle bir şeyi tespit etmesi çok kolay olurdu.