具有代数类型的镜头包

时间:2013-04-25 03:33:40

标签: haskell lenses

我正在使用镜头包进行编码。一切都很顺利,直到我试图访问代数类型的某个字段:

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的monoid作为数据类型而不是Char,那么它会编译并给出正确的答案,我不知道为什么。

编辑:在阅读了hammar的评论之后,我尝试了这个,这有效:

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

但是,对于那些必须存在的东西,可能会有一点奇怪。

1 个答案:

答案 0 :(得分:5)

只是为了回答这个问题,我的问题是我有一个代数类型,其中一些字段在不同的构造函数之间是共同的但是如果我试图使用它们,那么有一些未共享的字段会在运行时死掉

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会警告我。它似乎很容易发现这样的东西。