可组合式传感

时间:2016-02-14 16:38:36

标签: haskell lens lenses

我有以下Haskell类型

import Control.Lens
import Data.Map.Lens
import qualified Data.Map as M

type RepoStats = M.Map String (M.Map String RepoStat)
data RepoStat = RepoStat {
  _full_name :: String,
  _bug_count :: Maybe Int,
  _commit_count :: Maybe Int
  }
  deriving (Generic, Show, Eq, Read)
makeLenses ''RepoStat

我希望能够抽象出对值的嵌套检查并给出默认值:

makeStat name bug commit =
    RepoStat
    { _full_name = name
    , _bug_count = Just bug
    , _commit_count = Just commit
    }

getRepoStat :: String -> String -> (RepoStat -> Identity RepoStat) -> RepoStats -> Identity RepoStats
getRepoStat k1 k2 = at k1 . non (M.empty) . at k2 . non (makeStat k2 0 0)

fakeMap = M.empty :: RepoStats
setBug = fakeMap & (getRepoStat "a" "b") . bug_count ?~ 4
getBug = fakeMap ^. (getRepoStat "a" "b") . bug_count

setBug编译并运行,但getBug没有。

test/Spec.hs:65:21: Couldn't match type ‘Identity RepoStats’ …
                   with ‘Const RepoStat RepoStats’
    Expected type: Getting RepoStat RepoStats RepoStat
      Actual type: (RepoStat -> Identity RepoStat)
                   -> RepoStats -> Identity RepoStats
    In the second argument of ‘(^.)’, namely ‘(getRepoStat "a" "b")’
    In the expression: fakeDb ^. (getRepoStat "a" "b")
Compilation failed.

是否有可能以这种方式抽出镜头?

2 个答案:

答案 0 :(得分:3)

确实如此。您为getRepoStat指定的类型签名是其类型签名setter。如果您希望能够将其用作lens的完整通用性,那么类型签名应为

getRepoStat :: Functor f => String -> String -> (RepoStat -> f RepoStat) -> RepoStats -> f RepoStats
-- same definition

这可能也是getRepoStat的推断类型。

答案 1 :(得分:1)

删除类型签名非常有用:

getRepoStat k1 k2 = at k1 . non M.empty . at k2 . non (makeStat k2 0 0)

现在我们可以在ghci中查看带有:t getRepoStat的推断类型。

它有点乱,主要是因为at超载:

getRepoStat
  :: (Functor f, At m, IxValue m ~ M.Map String RepoStat) =>
     Index m -> String -> (RepoStat -> f RepoStat) -> m -> f m

我们可以识别,但我们会返回Lens',我们也可以将镜头的上下文限制为RepoStats

getRepoStat :: String -> String -> Lens' RepoStats RepoStat
getRepoStat k1 k2 = at k1 . non M.empty . at k2 . non (makeStat k2 0 0)