Как использовать FSharpPlus.Lens для указания индекса списка?

Пример кода документации определяет _pageNumberс использованием List._item, но я не могу найти пример его использования. Я попробовал следующий код, но он выдал ошибку.

      view (Book._pageNumber 1) rayuela // error

Как это будет использоваться?

2 ответа

Я вижу то же самое:

Нет перегрузок, соответствующих методу «Ноль».

Проблема вызвана тем, _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, которое не было учтено в документации. Если вы хотите обойти проблему, вы можете определить себя, и тогда пример будет скомпилирован:

      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#+, просто так ведет себя объектив.

Некоторая предыстория: призмы или частичные линзы подобны линзам, которые могут выйти из строя, поэтому в принципе вы не можете использовать операцию над ними, потому что это операция, которая всегда завершается успешно, или, лучше сказать, не считается ошибкой, вы должны использовать операция, которая возвращает параметр.

Правила композиции гласят, что результат композиции:

  • линза с линзой это линза
  • линза с призмой (или наоборот) это призма
  • призма с призмой это призма

То есть, как только в цепочке композиции есть призма, результатом будет призма.

В нашем случае имеем которые представляют собой линзу, состоящую из линзы, состоящую из это призма, поэтому будет призма.

Теперь, что произойдет, если вы используете представление для призмы? Значение представляет собой сбой, например, нулевое значение параметра равно None, но здесь не указано нулевое значение.

Брайан прав в том, что сообщение об ошибке вводит в заблуждение, лучшей ошибкой было бы «не использовать вид через призму», но вместо этого происходит попытка получить голое значение (не внутри параметра), которое может представлять сбои с .

тл; ДР

используйте вместо этого:

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

Кто-то должен отправить PR, чтобы добавить эту строку в документы.

Другие вопросы по тегам