文档的示例代码使用List._item
定义了_pageNumber
,但我似乎找不到它的使用示例。 我尝试了以下代码,但它给出了一个错误。
view (Book._pageNumber 1) rayuela // error
它将如何使用?
我看到了同样的事情:
没有与方法'零'匹配的重载"。
该问题是由_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是一种非常非常微妙的野兽。这是一个有趣的实验,但它很容易破裂。当它中断时,编译器错误令人难以置信。
从技术角度来看,布莱恩的回答非常准确,但在概念上忽略了最重要的一点:您正在"查看"部分镜头(也称为棱镜),而不是"预览"它。这不是F#+的限制,这只是镜头的行为方式。
一些背景:棱镜或部分镜头就像一个可能会失效的镜头,所以原则上你不能对它们使用view
操作,因为这是一个总是成功的操作,或者更好地说不考虑失败,你应该使用preview
操作返回一个选项。
合成规则规定,合成的结果:
- 带镜头的镜头是镜头 带棱镜
- (或相反)的透镜是棱镜 棱
- 镜的棱镜是棱镜
也就是说,一旦组合链中有一个棱镜,结果就会是一个棱镜。
在我们的例子中,我们有_pages << List._item i << _Some
透镜,透镜由棱镜组成的透镜组成_Some
,因此_pageNumber i
将是棱镜。
现在,如果您使用视图作为棱镜会发生什么?zero
值表示失败,例如,选项的零值为 None,但此处未指定零值。
Brian是对的,因为错误消息具有误导性,更好的错误是"不要在棱镜上使用视图",而是尝试获取一个裸值(不在选项内),这可以表示zero
失败。
TL;博士
请改用:
preview (Book._pageNumber 1) rayuela // Some { Contents = "The End" }
有人应该发送 PR 以将该行添加到文档中。