使用镜头库构建复杂功能

时间:2014-12-08 12:40:14

标签: haskell lens

我试图实现一个具有lens参数的函数,并且mconcat Maybe一个(->) r幺半群具有z pom root [(textFrom "groupId"), (textFrom "artifactId"), (textFrom "version")] textFrom :: Text -> Document -> Lens' Document Element -> Maybe Text textFrom name pom ln = listToMaybe $ pom ^.. ln ./ ell name . text 类型的两个应用效果。我可能遗漏了一些基础知识,所以对分解这个问题的任何帮助表示赞赏。

我想写'用户'避免传递相同参数的代码,如下所示:

Prelude Data.Monoid Control.Applicative> let f a b = Just $ show (a+1) ++ b
Prelude Data.Monoid Control.Applicative> let z a b xs = mconcat $ ($ b) <$> ($ a) <$> xs
Prelude Data.Monoid Control.Applicative> z 3 "x" [f,f]
Just "4x4x"

这是我尝试过的。没有镜头的概念:

z :: (forall f. Functor f, Monoid b)  => Document -> ((Element -> f Element) -> Document -> f Document) -> [Document -> ((Element -> f Element) -> Document -> f Document) -> b] -> b
z pom ln xs = mconcat $ ($ ln) <$> ($ pom) <$> xs

带镜头:

    Couldn't match expected type ‘[a0
                               -> Document
                               -> [Document
                                   -> ((Element -> f Element) -> Document -> f Document) -> b]
                               -> b]’
            with actual type ‘(Element -> f Element) -> Document -> f Document’
Relevant bindings include
  xs :: (Element -> f Element) -> Document -> f Document
    (bound at Main.hs:27:10)
  z :: (forall (f1 :: * -> *). Functor f1)
       -> Monoid b =>
          Document
          -> ((Element -> f Element) -> Document -> f Document)
          -> [Document
              -> ((Element -> f Element) -> Document -> f Document) -> b]
          -> b
    (bound at Main.hs:27:1)
Probable cause: ‘xs’ is applied to too few arguments
In the second argument of ‘(<$>)’, namely ‘xs’
In the second argument of ‘($)’, namely ‘($ ln) <$> ($ pom) <$> xs’

但它无法编译:

{{1}}

1 个答案:

答案 0 :(得分:2)

有时,将镜头存放在容器中会导致问题,但我们可以使用ALens'来规避这些问题。从概念上讲,ALens' Element DocumentFunctor f => ((Element -> f Element) -> Document -> f Document)或多或少相同,但您可以将其放入容器中,问题较少。需要注意的一点是,每个镜头都是普遍适用于所有Functor的镜头,因此原始签名实际上看起来应该更像这样(虽然这不太合适):<​​/ p>

z :: (Monoid b)
  => Document
  -> (forall f. Functor f => (Element -> f Element) -> Document -> f Document)
  -> [Document -> (forall f. Functor f => (Element -> f Element) -> Document -> f Document) -> b]
  -> b

如果我们使用zALens'一个类型,我们最终得到这个(假设您正在使用lens库。如果没有,请参阅底部的注释):< / p>

 z :: (Monoid b)
   => Document
   -> ALens' Element Document
   -> [Document -> ALens' Element Document -> b] 

使用这个新签名,您提供的原始定义将起作用。

我们可以使用不同的Monoid实例来简化此定义。 Monoid b => (a -> b) Monoid实例组合了具有Monoid结果类型的函数。这是一个例子:

lengthSum :: [a] -> Sum Int
lengthSum xs = Sum (length xs)

λ> (lengthSum <> lengthSum) "abc"
Sum {getSum = 6}

(<>)方法mappend是将每个函数应用于给定参数的结果(因此它最终与length "abc" + length "abc"基本相同)。同样,mconcat将组合函数列表的结果。这也扩展到多个参数的函数,只要每个函数是相同的类型,结果类型是Monoid的实例。

有了这个实例,我们可以定义:

z pom ln xs = mconcat xs pom ln

此定义适用于该类型的镜头和非镜头版本。

如果您没有使用lens库,则应该可以将ALens'定义为

newtype ALens s t a b = ALens
  { getLens :: forall f. Functor f => (a -> f b) -> s -> f t
  }
type ALens' s a = ALens s s a a