Posts Tagged ‘websharper’
Коротко про WebSharper и сайтлеты
Этот пост – про WebSharper в целом и про сайтлеты в частности. Как я уже писал, WebSharper – это идея создания сайтов полностью на языке F#. У этой идеи есть несколько модельных реализаций, а точнее три – через Asp.Net, Asp.Net MVC и так называемые сайтлеты “sitelets”.
Концепция
Меня во всей этой концепции интересуют именно сайтлеты. В отличии от использования Asp.Net-конструктов (пусть и на F#), сайтлеты – наиболее “трушная” концепция, в которой сгенерированный код будет полностью отвязан от Asp.Net и, впоследствии, от IIS.
Теперь о том как это делается. На самом деле, основной концепцией является конверсия F# в JavaScript/CSS/HTML на этапе компиляции. Делается это за счет аттрибута под названием ReflectedDefinitionAttribute
который, имея alias под названием JavaScriptAttribute
, используется перед методами, которые нужно сконвертировать в JavaScript.
Что этот аттрибут делает? Он заставляет компилятор F# сохранить логические конструкты метода вместо того чтобы просто его компилировать. А тут включается постпроцессинг WebSharper’а, который берет это “отраженное определение” и, на его основе, формирует некий промежуточный слой JavaScript. Промежуточный слой нужен потому, что мэпить F# на JavaScript не так-то просто.
Простой код который вы напишете вот так
[<JavaScript>] let rec Fact n = match n with | n when n < 2 -> 1 | n -> n * Fact1 (n - 1)
может быть в последствии вызван со страницы напрямую – ведь все остальное, что есть на странице тоже определено через F#, конвертированный в JS и HTML.
Вызовы серверных методов
Мне очень понравилось что механизм вызова методов очень прозрачный. Метод помеченный как [<JavaScript>]
вызывается как JS прямо у клиента, в то время как метод помеченный как [<Rpc>]
хостится на сервере (как это будет сделано в полноценных сайтлетах – непонятно), и соответственно его вызов со страницы дергает сервер.
На данный момент механизм поведения Rpc-методов такой: если метод возвращает какое-то значение, то вызов является блокирующим. Если метод возвращает unit
– его вызов считается асинхронным и тем самым, если вы повесили на него брейкпоинт (да, это можно делать), вы можете остановиться в нем не тогда, когда ожидаете.
Формирование контента
ВебШарпер не отказывается от идеи шаблонов! По крайней мере сайтлеты на текущем этапе предоставляют возможность писать шаблоны в HTML, который потом компилируется… в F#! Что намного более извращенно чем то, что делает Asp.Net MVC, например. Но мы сейчас не об этом.
Дело в том, что сам по себе WebSharper предоставляет возможность формировать HTML путем использования HTML-подобных конструктов в F#. Механизм, с помощью которого организовано создание HTML весьма забавно. В модуле IntelliFactory.Html.Tags
есть определения для большого количества тэгов, например H1
. Каждый тэг – это функция, которая сама по себе берет список, состоящий из нескольких “нодов”, которые могут формировать как аттрибуты данного тэга, так и его начинку. Например, чтобы сделать заголовок с текстом, мы пишем следующее:
H1 [Text "Title"]
В примере выше, метод Text
определяет, что его аргумент – это действительно текст. Вместо него, мы бы могли например использовать Attr
чтобы указать что это аттрибут.
Теперь насчет композиции. Композиция – это центральная особенность ВебШарпера, которая очень хорошо выделяет его по сравнению с тем же Asp.Net MVC. В данном случае, для вкладывания HTML-элементов друг в друга, мы можем использовать оператор -<
. Например:
Div [Attr.Style "padding: 5px;"] -< [ P [Text "Hello"] ]
События
Обработка событий в F# делается путем еще одного полезного оператора |>!
. Этот оператор делает подписку на событие, и одновременно возвращает созданный элемент, что обязательно в условиях механики работы WebSharper. Вот небольшой пример:
let update () = // обработка события let input = Input [Attr.Type "Text"] Form [ label "First Name: " input |>! OnKeyUp (fun _ _ -> update ()) ]
Опять же, интересно тут то, что не важно, где располагается метод update()
– на сервере или на клиенте.
Это вам не MVC!
Сейчас устраивать гонения на MVC модно, многие уходят на Rails (в т.ч. всем известный Рой Ошеров), поэтому я решил тоже подлить масла в огонь хотя, по-хорошему, ВебШарпер является частью Asp.Net инфраструктуры. Но это пока сайтлеты полностью не дописаны. Так вот, понятное дело, что в функциональной парадигме, создание сайтов выглядит несколько по-другому.
Для начала, давайте познакомимся с концепцией “формлетов”. Формлет – это возможность описания того, какие данные нужно получать в форме и как пройдет валидация. Например:
[<JavaScript>] let BasicInfoForm () : Formlet<BasicInfo> = Formlet.Yield (fun name age -> { Name = name; Age = age }) <*> Input "Name" "Please enter your name" <*> InputInt "Age" "Please enter a valid age"
Формлеты можно “обрамлять” (типичный паттерн Декоратор) в различные другие конструкты, функционально добавляя им кнопки Submit/Reset, настройки, и так далее. Формлеты можно инстанциировать у себя в коде, причем с ними можно делать вещи, которые в том же MVC делать очень сложно. Например, допустим у вас сайт где можно разместить резюме, и вы хотите чтобы человек мог внести данные о всех местах где он работал. Для этого, вы делаете формлет, который инкапсулирует все данные о работе (где, когда, итп.) а потом одной строчкой кода говорите “их может быть несколько”. И все! Композиционная структура сделает все сама за себя.
Так вот, о чем мы? После того как мы сделали несколько формлетов, их можно выстроить в цепочку, так что нажав Submit на одном, мы перейдем к другому. Эта “концепция” именуется flowlet’ом (или flowlet’ами). Выражается это следующим образом.
Во-первых, есть некий workflow под названием Flowlet.Do
этот вокрфлоу умеет показывать формы в тот момент, когда они учавствуют по правую сторону let!
, и сохранять их возвращенные значения (да-да, форма это функция, а ее return value – это то, что пользователь ввел). После этого, путем return!
происходит “типа возврат” из этой цепочки, то есть мы можем, например, предоставить фунцию, которая получит результаты formlet’ов и выведет из них полезные значения на экран.
На самом деле, механика всего этого чуть-чуть сложнее. Во-первых, конечным результатом выражения Formlet.Do
все равно должен быть Formlet
, то есть создав элемент, вам придется его конвертировать. Но это не сложно! А во-вторых, весь этот computational expression должен быть передан в функцию Formlet.Flowlet
для того чтобы оркестровать такое феерическое действие.
Formlet.Do { let! w = welcomeForm let! c = consentForm return! Formlet.OfElement (proc w c) } |> Formlet.Flowlet
Представление
На данный момент презентация сего фееричного действия организована в терминах Asp.Net’ного WebControl
, от которого наследует ВебШарперный контрол, давая нам возможность засунуть в него целиком наш pagelet. Pagelet – это еще одна абстракция в стиле “нечто что может фигурировать на странице”. Логика мне не ясна, но на данный момент pagelet’ом являются текст, Element
(это просто элемено HTML) или Flowlet
.
Понятное дело, что формирование отдельного WebControl
-а не означает создание странички целиком. И вот тут начинаются странности.
Дело в том, что для сайтлетов у нас генерируются 3 проекта, а именно:
- Проект Asp.Net, который содержит некоторые ресурсы (например CSS), но в будущем скорее всего не будет нужен вообще. По крайней мере, сейчас для сайтлетов он служит заглушкой со ссылкой на “настоящий сайт”.
- Проект сожержащий сайтлет.
- Проект с шаблонами.
Проект с шаблонами – это возможность делать, фактически, master-странички со вставками, определеными в F#. Вы уже догадались почему нужно конвертировать HTML в F#? Поясню: в основном проекте, у нас появляется возможность строготипизированно определить, что мы помещаем в различные секции шаблона. Обычно туда помещяется либо наш F#-созданный HTML, либо… наш WebControl
, конечно же!
За этим всем стоит разве что само определение сайта. Там собраны все шаблоны, и еще есть некий plumbing для того чтобы все работало.
Размышления
Для меня, идеальным кажется сайт, который существует целиком на клиенте, а с сервером общается только через REST или более продвинутую парадигму a la NetKernel. Поэтому в случае с ВебШарпером (надеюсь мне авторы простят русификацию названия), то чего хочу видеть я – это полную отвязку от Asp.Net и IIS. Я точно не знаю когда это будет, но коллеги подсказывают мне, что релиз WebSharper 2.0 будет отвязан от Asp.Net, в то время как полноценная поддержка сайтлетов с отвязкой от IIS будет в последующем релизе. Главное, что для перехода на Linux (а это основная цель) не надо будет ничего переписывать. По крайней мере я на это надеюсь.
Теперь что касается юзабельности сего фреймворка. Понятное дело что вкуривать сложную DSL и писать на F# – занятие не для слабонервных, но лично меня технические трудности не очень беспокоят. Композициональность, если так можно выразиться, фреймворка впечатляет, ровно как и его возможность быстро оборачивать такие JS библиотеки как JQueryUI, YUI или даже RxJS! Это означает что в F# можно получить унифицированную, строготипизированную модель, которая будет сама подсказывать как использовать тот или иной фреймворк и упрощать работу с ним.
Анонсы
Коротко о главном. Во-первых, Петербургская Группа Alt.Net планирует провести 3х-часовую встречу посвещенную F#. Максим Моисеев и я будем рассказывать про все, начиная с основных понятий и заканчивая спекуляциями на тему type provider’ов. Встреча пройдет 3го Февраля в офисах Exigen, и в ближайшее время мы вывесим анонс на сайтах Ineta и Spbalt.net.
Во-вторых, на выходных периодически происходят неформальные встречи участников сообщества. Встречи происходят в моем офисе (территориально 3 мин. от м. Автово), так что если вы живете в Питере и у вас есть желание убить выходной копаясь в F# (или еще какой-нть интересной фигне) с единомышленниками, напишите мне и мы обсудим. Писать лучше по скайпу — dmitri.nesteruk
.
На этом все. Comments welcome!