如何使用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 以将该行添加到文档中。
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 以将该行添加到文档中。