Дмитpий Hecтepук

Блог о программировании — C#, F#, C++, архитектура, и многое другое

Posts Tagged ‘websharper

Коротко про WebSharper и сайтлеты

leave a comment »

Этот пост – про 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!

Реклама

Written by Dmitri

19 января 2011 at 23:53

Опубликовано в .NET, f#

Tagged with ,