Помогите с описанием типов

101 views
Skip to first unread message

Артур Файзрахманов

unread,
Jan 16, 2015, 9:30:59 AM1/16/15
to haskell...@googlegroups.com

Всем привет!

Я пишу веб—приложение с ипользованием Yesod'а для серверной части, вместо ЯваСкрипта для клиентской части, я пишу код на Хаскеле и перевожу его в ЯваСкрипт с помощью GHCJS.

В приложении есть несколько форм, например, таких как форма входа и форма регистриации. Моя конечная цель — написать некую функцию, которая на входе получает форму, а на выходе выдаёт некий проверщик данных. Задумка в том, чтобы упомянутая функция сначала осуществляла поиск всех (стандартных) полей ввода в форме, и на основе атрибутов и типа поля назначала каждому полю своего проверщика (к примеру, Проверщик ПароляПроверщик ЭлПочты и т.д.), итог проверки всей формы должен быть суммированием итогов проверки каждого её поля ввода. Эту функцию я ещё не написал.

По задумке, ПроверщикДанных — это некий новый тип:

новыйтип ПроверщикДанных д =
        ПроверщикДанных { получитьПроверщик :: Проверщик д }

По своей природе, он реактивный, то есть входные данные — это не одиночная величина, а некоторая последовательность. Есть некая функция, которая получает на входе ПроверщикДанных и возвращает текущий итог проверки:

выполнитьПроверку :: ПроверщикДанных д -> ИтогПроверки'

Итог проврки может быть одним из трёх: данные не проверялись (нет входящего значения), данные неверные (данные не прошли проверку) и данные верные (данные прошли проверку). Сразу отмечу несколько вещей: в случае неверных данных я хочу предоставлять некое описание причины, по которой данные не прошли проверку; в случае успешной проверки данных, я думаю, нужно передавать проверенное значение. По поводу последнего утверждения я ещё до конца не уверен, но интуиция подсказывает, что так должно быть, к примеру, если во время проверки данные подвергаются изменениям, — предположим, проверяется строковая величина с такими условиями проверки: строка не может состоять только из пробельных символов; строка должна начинаться и заканчиваться непробельным символом. В таком случае, функция проверки производит обрезку строки, и очень логично, вернуть в итоге преобразованную, т.е. обрезанную строку. Описать такой тип дынных можно так:

тип ИтогПроверки д причина = НеПроверенно
                           | НеверныеДанные причина
                           | ВерныеДанные д

Получается два параметра: тип данных д и тип сообщения причина. Можно избавиться от последнего параметра, но тогда нет никакой возможности описать собственную функцию проверки с собственными сообщениями, т.е. пользователь будет ограничен неким стандартным набором сообщений, что неприемлимо. Эти парметры так же создают другие проблемы, первая из них в том, что ПроверщикДанных теперь тоже должен иметь параметр для указания типа, представляющего причину отрицательной проверки:

новыйтип ПроверщикДанных д причина =
        ПроверщикДанных { получитьПроверщик :: Проверщик д причина }

Что же с суммированием итогов проверки? Всё очень просто, когда мы суммируем итоги проверки с одинаковыми параметрами, но в оригинальной задумке параметры почти всегда разные:

пусть п1 :: ПроверщикДанных Почта ПричинаПочта
      п2 :: ПроверщикДанных Пароль ПричинаПароль
      пф :: ПроверщикДанных Форма ПричинаФорма

Как же суммировать итоги проверки п1 и п2 так, чтобы получить пф? По задумке, при запросе итога проверки формы в ответ ожидаются следующие варианты:

  • НеПроверено
  • НеверныеДанные [приина1, причина2,.. ] — форма может не пройти проверку из—за ошибок сразу нескольких полей
  • ВерныеДанные (данные1, данные2,.. ) — в случае, когда все данные достоверные, было бы замечательно возвращать величины каждого поля ввода (например, чтобы сформировать из них некий конечный результат определённого типа)

Сразу видны основные проблемы: причины могут быть разного типа, поэтому я не могу просто взять и объединить их в список; различие типов не проблема в случае кортежей, но я не знаю, как создавать кортежи из неопрделенного количества элементов. 

Подскажите, куда двигаться? Как обойти описанные проблемы?

Текущий вариант библиотеки здесь. Сейчас ИтогПроверки не возвращает никакие данные в случае успешной проверки, а в качестве причины отрицальтельной проверки используется только один тип — ValidationMessage.

P.S. Прошу прощения за длинный текст, непонятный заголовок и скомканные вопросы.

Alexander Tchitchigin

unread,
Jan 16, 2015, 9:55:11 AM1/16/15
to haskell...@googlegroups.com
А Вы на Хаскеле прямо по-русски пишете? ИтогПроверки, тип, новыйтип - прямо так?


--
Вы получили это сообщение, поскольку подписаны на группу "Русский Haskell".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес haskell-russi...@googlegroups.com.
Чтобы отправлять сообщения в эту группу, отправьте письмо на электронный адрес haskell...@googlegroups.com.
Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/haskell-russian/cd7894b0-0b0a-4288-aa07-b64119caa72c%40googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.



--
С уважением,
Александр.

Артур Файзрахманов

unread,
Jan 16, 2015, 10:04:44 AM1/16/15
to haskell...@googlegroups.com
А Вы на Хаскеле прямо по-русски пишете? ИтогПроверки, тип, новыйтип - прямо так?
 
Нет, конечно, просто для удобства чтения решил порусски написать, если так читать труднее могу излагать код поанглийски (вообще иметь возможность писать на Хаскеле порусски — это пока невоплощенная мечта). 

Arthur Welf

unread,
Jan 16, 2015, 10:06:35 AM1/16/15
to haskell...@googlegroups.com
А почему у вас описание причин ошибки разного типа? Вы разве выводите их пользователю не в виде строки? В этом случае у вас описание нескольких причин ошибки - это список строк, каждая из которых содержит одно описание причины ошибки. 

-- 
Best regards,
Arthur Welf

пятница, 16 января 2015 г. в 16:30, Артур Файзрахманов написал:

--

Артур Файзрахманов

unread,
Jan 16, 2015, 10:30:29 AM1/16/15
to haskell...@googlegroups.com
А почему у вас описание причин ошибки разного типа? Вы разве выводите их пользователю не в виде строки? В этом случае у вас описание нескольких причин ошибки - это список строк, каждая из которых содержит одно описание причины ошибки. 

Всё просто, когда ошибки представлены просто строкой или текстом, а само приложение рассчитано только на один язык. Я же хочу представлять сообщение об ошибке неким типом, а перевод сообщения, скажем, в Текст выполнять с помощью специальных функций, т.е. заранее предоставить возможность переводить сообщения на разные языки (https://github.com/geraldus/DataValidation/blob/master/src/Data/Validator/I18n/Message.hs). Я позаимствовал эту идею в Yesod — https://github.com/yesodweb/yesod/blob/master/yesod-auth/Yesod/Auth/Message.hs#L25.

Dmitry Dzhus

unread,
Jan 28, 2015, 3:20:44 PM1/28/15
to haskell...@googlegroups.com
пятница, 16 января 2015 г., 18:30:29 UTC+3 пользователь Артур Файзрахманов написал:
А почему у вас описание причин ошибки разного типа? Вы разве выводите их пользователю не в виде строки? В этом случае у вас описание нескольких причин ошибки - это список строк, каждая из которых содержит одно описание причины ошибки. 

Всё просто, когда ошибки представлены просто строкой или текстом, а само приложение рассчитано только на один язык. Я же хочу представлять сообщение об ошибке неким типом, а перевод сообщения, скажем, в Текст выполнять с помощью специальных функций, т.е. заранее предоставить возможность переводить сообщения на разные языки (https://github.com/geraldus/DataValidation/blob/master/src/Data/Validator/I18n/Message.hs). Я позаимствовал эту идею в Yesod — https://github.com/yesodweb/yesod/blob/master/yesod-auth/Yesod/Auth/Message.hs#L25.

Для этого совсем не требуется вводить разные *типы* для сообщений об ошибках.
Ошибка остаётся одним-единственным типом с большим набором конструкторов,
каждый из которых соответствует конкретной ошибке. В процитированном вами 
примере из Yesod как раз это и сделано. Хотя и сгулять от конструкторов до типов
и обратно тоже несложно (используя типы-одиночки (singletons)), но тут для этого
не видно достаточного обоснования.

Иными словами, не видно /причин/, почему бы тут следовало использовать разные
типы ПричинаПочта, ПричинаПароль, ПричинаФорма. Более того, такая структура
по всей видимости предполагает, что ошибка зависит лишь от типа проверяемого поля.
А ведь было бы удобно иметь возможность кинуть для поля типа «почта» разные ошибки,
например:

1. Когда оно является обязательным, но не заполнено
2. Когда оно заполнено, но не соответствует формату (например, в нём два символа @)
3. Когда оно заполнено по формату, но принадлежит недопустимому домену

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

А дальше вы будете решать проблему, как элегантнее обеспечить расширяемость
с двух сторон набора N сообщений на M языках :)

Также я не уверен, что удобно будет пользоваться результатами проверки в виде
простого списка ошибок. Ведь так теряется информация о том, где случилась ошибка.
Для примера можно посмотреть, как сделано в digestive-functors.

Также я считаю ужасной идею объединять проверку данных и их вычистку в одной функции.
Reply all
Reply to author
Forward
0 new messages