2

我正在尝试实现一个函数,该函数有一个lens参数,并且将mconcat是一个Maybe具有 type 的两个应用效果的 monoid (->) 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’
4

1 回答 1

2

有时,将镜头存放在容器中可能会导致问题,但我们可以通过使用ALens'. 从概念上讲,ALens' Element Document或多或少与 相同Functor f => ((Element -> f Element) -> Document -> f Document),但您可以将其放入问题较少的容器中。需要注意的重要一点是,每个镜头在所有Functors 上都是通用的,因此原始签名实际上应该看起来更像这样(尽管这不太有效):

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

如果我们z使用 using 给出一个类型ALens',我们最终会得到这个(假设您正在使用该lens库。如果没有,请参阅底部的注释):

 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
于 2014-12-08T15:59:12.203 回答