Моя компания попросила меня написать специальное приложение, которое позволяло бы пользователям выполнять специальные запросы к базе данных с плоскими файлами. Пользователи этого приложения были ваши типичные Джо Бизнесмен типа. Они не программисты, и вряд ли они когда-либо видели оператор SQL в своей жизни.
В результате мне было поручено разработать дружественный пользовательский интерфейс, который позволял бы пользователям выбирать столбцы, таблицы, условия и т. Д. Для построения запроса. Это сложно, потому что я могу представлять оператор SQL в пользовательском интерфейсе, не создавая его абстрактное представление в памяти.
Первая итерация была написана на C #. Я создал классы загрузки, чтобы представить абстрактный синтаксис оператора SQL, что привело к действительно громоздкой объектной модели:
- класс Join, класс коллекции Joins
- класс WhereClause, класс коллекции WhereClauses
- класс SelectedColumn, класс коллекции SelectedColumns
- класс OrderBy, класс коллекций OrderBy
- класс SqlStatement, который сгруппировал все вышеупомянутые классы вместе
Преобразование экземпляра SqlStatement в строку было великолепно болезненным, уродливым и глючным. Перемещение противоположного направления, от строки к SqlStatement, было еще хуже, поскольку оно разбивало куски строки SQL с помощью большого количества регулярных выражений и манипуляций со строками.
Я взломал систему, создал приложение, которое работало, но я был не очень доволен этим. Особенно меня не было, когда менялись бизнес-требования приложения, что заставляло меня пересмотреть мой код C #.
Так же, как эксперимент, я переписал свой SqlStatement на F # и представил его как объединение:
type dir = Asc | Desc
type op = Eq | Gt | Gte | Lt | Lte
type join = Inner | Left | Right
type sqlStatement =
| SelectedColumns of string list
| Joins of (string * join) list
| Wheres of (string * op * string) list
| OrderBys of (string * dir) list
type query = SelectedColumns * Joins * Wheres * OrderBys
Этот небольшой объем кода заменил несколько сотен строк C # и дюжину или около того классов. Что еще более важно, сопоставление с образцом упростило процесс, необходимый для преобразования абстрактного представления в строку SQL.
Самое интересное - преобразовать строку SQL обратно в объект запроса с помощью fslex / fsyacc.
Если я правильно помню, исходный код C # насчитывал 600 строк и около десятка классов, много беспорядочных регулярных выражений и требовалось два дня для написания и тестирования. Для сравнения, код F # состоял из одного файла .fs, состоящего из 40 строк, примерно 100 строк, для реализации лексера / парсера, и потратил несколько часов на тестирование.
Серьезно, написание этой части приложения на F # казалось обманом, вот как это было легко.