2013-04-25 9 views
13

Kodowałem przy pomocy pakietu soczewek. Wszystko szło dobrze, dopóki nie próbował uzyskać dostęp do jakiegoś pola na typ algebraicznych:Pakiet obiektywów z typami algebraicznymi

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 

może po prostu iść z _a, ale typ danych w moim prawdziwym programem jest znacznie głębszy i rodzaju przeznaczone na użyciu obiektywu zmniejszyć ilość pracy, którą muszę wykonać. Przeglądam bibliotekę obiektywów, ale jest ich tak wiele i nie jestem pewien, czy ma do czynienia z tym scenariuszem, czy jest to po prostu coś, czego biblioteka obiektywów nie obsługuje.

Na marginesie, jeśli faktycznie używam monoidu takiego jak String dla typu danych zamiast Char, to kompiluje i daje właściwą odpowiedź, nie mam pojęcia dlaczego.

Edycja: Po przeczytaniu komentarza hammar za, próbowałem to i to działa:

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

Ale to jest trochę dziwne, aby uzyskać może się z tego czegoś, co ma istnieć.

+5

Niezbyt dobrze zna wewnętrzne działanie pakietu soczewek, ale: Zastanów się, co "B ^. a' powinien powrócić. Musi wybrać coś, więc próbuje użyć 'mempty' jako domyślnego zamiast wyrzucania wyjątku takiego jak' _a B'. – hammar

+2

Nie miałem pojęcia, że ​​_ b skompiluje. Miałem błąd w moim kodzie, że nie zdawałbym sobie sprawy, że był tam do czasu uruchomienia i nie było żadnego ostrzeżenia o tym nawet przy Wall. Więc, sądzę, że użyję ^? i być może funkcja i to dokładnie to, czego potrzebuję. –

+3

Właściwie po namyśle doszedłem do wniosku, że metody dostępu do typów algebraicznych to po prostu zły pomysł. Jestem zaskoczony, że jest to dozwolone. Miałem wiele błędów w moim programie, które właśnie wyczyściłem. –

Odpowiedz

5

Po to, aby odpowiedzieć na to pytanie, mój problem polegał na tym, że miałem typ algebraiczny, w którym niektóre pola były wspólne dla różnych konstruktorów, ale w przypadku niektórych pól, które nie były udostępnione, umierali w czasie wykonywania, gdyby spróbowałem użyć im.

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) 

To kompiluje, ale umiera, jeśli popełnię błąd używając funkcji wagi. Zrozumiały błąd. Kiedy soczewki używają szablonu haskell do generowania wystąpień, zauważa to i zmienia jego zachowanie, aby zapobiec pomyłce. Można usunąć elementy uzupełniające pola, ale w moim przypadku większość pól była taka sama między typami danych. Oto jak powinien Pisałem typ danych raz zauważyłem pola nie dało się:

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 

Robiąc to w ten sposób, gdy jest nieco mniej czyste, błąd zostały złowione w czasie kompilacji. Zmuszając mnie do samodzielnego pisania funkcji, zauważyłbym jakiekolwiek częściowe funkcje, jak je pisałem.

Życzę sobie, że ghc ostrzegłby mnie. Wygląda na to, że byłoby naprawdę łatwo wykryć takie coś.