Подтвердить что ты не робот

Пакет объектива с алгебраическими типами

Я кодировал пакет объектива. Все шло хорошо, пока я не попытался получить доступ к определенному полю по алгебраическому типу:

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, но тип данных в моей реальной программе намного глубже, и я хотел бы использовать объектив, чтобы уменьшить объем работы, которую я должен выполнить. Я просматриваю библиотеку объективов, но там так много, и я не уверен, справился ли он с этим сценарием, или это просто то, что не поддерживает библиотека объективов.

В качестве побочного примечания, если я действительно использую моноид как String для типа данных вместо Char, он затем компилирует и дает правильный ответ, я понятия не имею, почему.

Изменить: после прочтения комментария hammar, я пробовал это, и это работает:

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

Но это странно, чтобы получить, возможно, из этого для чего-то, что должно существовать.

4b9b3361

Ответ 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)

Это компилирует, но умирает во время выполнения, если я ошибаюсь в использовании функции веса. Понятная ошибка. Когда объективы используют шаблон 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 предупредил меня. Похоже, было бы очень легко обнаружить такую ​​вещь.