为列表创建镜头(如)

wor*_*shi 4 haskell lenses

我正在尝试为以下数据结构创建镜头.我正在使用lens-family.

data Tree = Tree {
        _text :: String,
        _subtrees :: [Tree]
    } deriving (Show,Eq,Read,Generic)
Run Code Online (Sandbox Code Playgroud)

我想出于各种原因避免使用Template Haskell.首先,它似乎不适用于我的ghc版本(7.8.3),这是另一个(超出范围)问题.

为记录制作镜头并不难.

text :: Lens' Tree String
text f (Tree text' subtrees') =
  (\text'' -> Tree text'' subtrees') `fmap` (f text')

subtrees :: Lens' Tree [Tree]
subtrees f (Tree text' subtrees') =
  (\subtrees'' -> Tree text' subtrees'') `fmap` (f subtrees')
Run Code Online (Sandbox Code Playgroud)

但似乎镜头系列没有列表的默认镜头.我认为这是可能的.有lens包裹.这些是我失败的尝试:

import Lens.Family2
import Lens.Family2.Unchecked
-- List lenses
_last :: Lens [a] [a'] a a'
_last f l =
  -- first try
  --lens getter setter
  -- second try
  (\el -> (init l) ++ el) `fmap`(f (last l))
  where
    getter = last
    setter l el = l ++ el
Run Code Online (Sandbox Code Playgroud)

他们都得到一个类似于这个的错误:

Could not deduce (a' ~ [a'])
    from the context (Functor f)
      bound by the type signature for
                 _last :: Functor f => LensLike f [a] [a'] a a'
      at editor.hs:22:10-27
      ‘a'’ is a rigid type variable bound by
           the type signature for
             _last :: Functor f => LensLike f [a] [a'] a a'
           at editor.hs:22:10
    Expected type: f [a']
      Actual type: f a'
    Relevant bindings include
      f :: a -> f a' (bound at editor.hs:23:7)
      _last :: LensLike f [a] [a'] a a' (bound at editor.hs:23:1)
    In the second argument of ‘fmap’, namely ‘(f (last l))’
    In the expression: (\ el -> (init l) ++ el) `fmap` (f (last l))
Run Code Online (Sandbox Code Playgroud)

我该如何定义_last镜头?

编辑:这是一个版本:

_last f l = (\el -> (init l) ++ [el]) `fmap`(f (last l))
Run Code Online (Sandbox Code Playgroud)

虽然,正如大卫/丹尼尔指出的那样,_last应该是一个遍历,而不是镜头.

Dav*_*vid 6

你缺少方括号el((++)两个参数的列表).如果你把它们放入,你的第一次尝试应该有效.你的类型也太笼统了.列表在Haskell中不是异构的,因此它们只能包含一种类型的值.

此外,正如Daniel Wagner所说,这不是一个真正的镜头,因为它是偏袒的.您链接的镜头文档已过期.目前的镜头库有_last作为Traversal避免了这个问题,因为Traversal可以有0个或多个目标,而不是必须恰好有1镜头.