具有代数类型的镜头包

Dav*_*aly 13 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
Run Code Online (Sandbox Code Playgroud)

我可以选择_a,但我的真实程序中的数据类型更深,我打算使用镜头来降低我必须做的工作量.我一直在查看镜头库,但那里有很多,我不确定他是否处理过这种情况,或者它只是镜头库不支持的东西.

作为旁注,如果我实际上使用类似String的monoid作为数据类型而不是Char,那么它会编译并给出正确的答案,我不知道为什么.

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

test2 = (A 'a') ^? a
test3 = B ^? a
Run Code Online (Sandbox Code Playgroud)

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

Dav*_*aly 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)
Run Code Online (Sandbox Code Playgroud)

如果我犯了使用权重函数的错误,这会编译但运行时运行.可以理解的错误.当镜头使用模板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
Run Code Online (Sandbox Code Playgroud)

通过这种方式,虽然它不太干净,但错误是在编译时捕获的.通过强迫我自己编写函数,我会注意到在写这些函数时的任何部分函数.

我有点希望ghc会警告我.它似乎很容易检测到这样的事情.