Пакет линз с алгебраическими типами
Я кодировал с помощью пакета линз. Все шло хорошо, пока я не попытался получить доступ к определенному полю алгебраического типа:
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
Я мог бы просто пойти с _a, но тип данных в моей реальной программе гораздо глубже, и я вроде как намеревался использовать lens, чтобы уменьшить объем работы, которую мне нужно сделать. Я просматривал библиотеку линз, но там так много всего, и я не уверен, имеет ли он дело с этим сценарием или это просто что-то, чего нет в библиотеке линз поддержка.
В качестве примечания, если я действительно использую моноидную строку для типа данных вместо Char, он затем компилируется и дает правильный ответ, я понятия не имею, почему.
Edit: прочитав комментарий хаммара, я попробовал это, и это работает:
test2 = (A 'a') ^? a
test3 = B ^? a
Но это довольно странно-получить "может быть" из этого для чего-то, что должно существовать.
1 ответ:
Просто чтобы ответить на этот вопрос, моя проблема заключалась в том, что у меня был алгебраический тип, где некоторые поля были общими между различными конструкторами, но была пара полей, которые не были разделены, умрут во время выполнения, если я попытаюсь их использовать.
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)
Это компилируется, но умирает во время выполнения, если я делаю ошибку, используя весовую функцию. Понятная ошибка. Когда Lens использует шаблон haskell для создания экземпляров, он замечает это и изменяет свое поведение, чтобы предотвратить ошибку. Вы можно было бы удалить методы доступа к полю, но в моем случае большинство полей были одинаковыми между типами данных. Вот как я должен был написать тип данных, как только я заметил, что поля не совпадают:
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
Делая это таким образом, хотя это немного менее чисто, ошибка будет поймана во время компиляции. Заставляя меня писать функции самостоятельно, я замечал бы любые частичные функции, когда я их писал.
Я бы хотел, чтобы ghc предупредил меня. Похоже, так оно и будет на самом деле легко для него обнаружить такую вещь.