如何使用FSharpPlus.Lens指定列表的索引?

How to use FSharpPlus.Lens to specify the index of a list?

the documentation的示例代码使用List._item定义了_pageNumber,但我似乎找不到它的使用示例。 我尝试了以下代码,但它给出了一个错误。

view (Book._pageNumber 1) rayuela // error

如何使用?

我看到了同样的事情:

No overloads match for method 'Zero'".

问题是由 _Some 镜头引起的,它不适用于记录类型,因为它们没有默认值(即“零”):

let inline _pageNumberOpt i b =
    _pages << List._item i <| b

let pageOpt = view (_pageNumberOpt 1) rayuela   // this is fine
let page = view _Some pageOpt                   // this doesn't work, because the input is an Option<Page>
let x = view _Some (Some 1)                     // this works, because the input is an Option<int>

这似乎是 FSharpPlus 中的一个限制,文档中没有说明。如果你想解决这个问题,你可以自己定义Page.Zero,然后这个例子会编译:

type Page =
    { Contents: string }
    static member Zero = { Contents = "" }

let page = view (Book._pageNumber 1) rayuela
printfn $"{page}"     // output is: { Contents = "The End" }

let noPage = view (Book._pageNumber 5) rayuela
printfn $"{noPage}"   // output is: { Contents = "" }

Page.Zero 仅当您请求不存在的页面时才会被调用,但无论如何编译器都需要它。

(FWIW,根据我的经验,FSharpPlus 是一个非常非常精致的野兽。这是一个有趣的实验,但它很容易崩溃。当它崩溃时,编译器错误是 mind-boggling。)

Brian 的回答从技术角度来看非常准确,但在概念上忽略了最重要的一点:您正在“查看”部分镜头(也称为棱镜),而不是“预览”它。这不是 F#+ 的限制,这正是镜头的行为方式。

一些背景:棱镜或部分透镜就像一个可能会失效的透镜,所以原则上你不能对它们使用 view 操作,因为它是一个总是成功的操作,或者更好的说法是不会'不考虑失败,您应该使用 preview 操作,其中 returns 一个选项。

排版规则规定排版结果:

  • 有镜头的镜头就是镜头
  • 带棱镜(或反之)的透镜就是棱镜
  • 有棱镜的棱镜就是棱镜

这是,只要合成链中有棱镜,结果就会是棱镜。

在我们的例子中,我们有 _pages << List._item i << _Some 由镜头组成,镜头由 _Some 组成,这是一个棱镜,所以 _pageNumber i 将是一个棱镜。

现在,如果您将视图用作棱镜,会发生什么情况? zero 值表示失败,例如选项的零值是 None,但这里没有指定零值。

Brian 是正确的,错误消息具有误导性,更好的错误是“不要使用棱镜视图”,而是尝试获取裸值(不在选项内)可以用 zero.

表示失败

TL;博士

改用:

preview (Book._pageNumber 1) rayuela // Some { Contents = "The End" }

有人应该发送 PR 以将该行添加到文档中。