Глава 10. Ограничение значений F#

Аннотация

Публикация
Учебник библиотеки FParsec
Дата
Ссылки

Когда вы начинаете писать собственные синтаксические анализаторы с помощью библиотеки FParsec или пытаетесь скомпилировать некоторые отдельные фрагменты кода из учебника, вы столкнетесь с особенностями компилятора, которые часто вызывают некоторые затруднения среди новых пользователей F# и FParsec: ограничение значений. В этом разделе мы объясним ограничение значений и способ его обработки в ваших программах FParsec.

Если вы найдете содержимое главы слишком техническим на данный момент, просто перейдите к следующей главе и вернитесь позже, когда вы в первый раз увидите сообщение компилятора, в котором упоминается value restriction.

Ограничение значений F# является причиной того, что следующий фрагмент кода не компилируется

open FParsec
let p = pstring "test"

Но следующий фрагмент скомпилирован без проблем (Предполагаем, что вы ссылались на две библиотеки FParsec):

open FParsec
let p = pstring "test"
run p "input"

Ошибка компилятора, сгенерированная для первого примера будет примерно следующая:

error FS0030: Value restriction.
The value 'p' has been inferred to have generic type
    val p : Parser<string,'_a>
Either make the arguments to 'p' explicit or,
if you do not intend for it to be generic, add a type annotation.

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

Проблема с первым примером выше заключается в том, что компилятор F# выводит значение p как имеющее неопределенное значение общего типа, хотя F# не допускает значение общего типа в этой ситуации. Функция pstring возвращает значение типа Parser<string, 'u>, где 'u - параметр типа представляющий собой пользовательское состояние типа CharStream. Так как в первом примере выше, нет значений содержащих этот параметр типа, компилятор указывает тип результата как Parser<string, '_a> для синтаксического анализатора p, где '_a представляет не определенный параметр общего типа.

Во втором примере эта проблема не возникает, поскольку использование p в качестве первого аргумента функции run определяет тип пользовательского состояния. Поскольку run принимает только синтаксические анализаторы типа Parser<'t, unit>, компилятор выводит определенный тип Parser<string, unit> для p.

В примере ниже предлагается два способа обработки ограничения значений в программах FParsec:

Часто бывает удобно определить аббревиатуры типа следующего вида:

type UserState = unit // Конечно не обязательно должен быть unit типом
type Parser<'t> = Parser<'t, UserState>

С такими аббревиатурами, типы аннотаций становятся такими же простыми, как

let p : Parser<_> = pstring "test"

Конечно, ограничение значения типа синтаксического анализатора до определенного типа - это только решение, если вам действительно не нужен общий тип. Если вам нужен общий тип, вам придется применять другие методы, как это описано, например, в документации по языку F# или в статье Finer Points of F# Value Restrictionen в блоге Дмитрия Ломова. Однако значения Parser FParsec (непараметрические функции синтаксического анализатора) обычно используются только в контексте конкретного использования синтаксического анализатора с фиксированным типом состояния пользователя. В этой ситуации ограничение типа действительно является подходящей мерой, чтобы избежать ошибок ограничения значения.