<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Дмитрий Нестерук &#187; f#</title>
	<atom:link href="http://nesteruk.wordpress.com/category/net/f/feed/" rel="self" type="application/rss+xml" />
	<link>http://nesteruk.wordpress.com</link>
	<description>Блог о программировании — C#, F#, C++, архитектура, и многое другое</description>
	<lastBuildDate>Mon, 28 May 2012 09:47:56 +0000</lastBuildDate>
	<language>ru</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='nesteruk.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://0.gravatar.com/blavatar/46e0cad6558c5915dd7536a1281e4978?s=96&#038;d=http%3A%2F%2Fs2.wp.com%2Fi%2Fbuttonw-com.png</url>
		<title>Дмитрий Нестерук &#187; f#</title>
		<link>http://nesteruk.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://nesteruk.wordpress.com/osd.xml" title="Дмитрий Нестерук" />
	<atom:link rel='hub' href='http://nesteruk.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Парсим HTML Zen на F#, FsLex и FsYacc</title>
		<link>http://nesteruk.wordpress.com/2011/03/03/parsing-htmlzen-fsharp-fslex-fsyacc/</link>
		<comments>http://nesteruk.wordpress.com/2011/03/03/parsing-htmlzen-fsharp-fslex-fsyacc/#comments</comments>
		<pubDate>Thu, 03 Mar 2011 12:22:51 +0000</pubDate>
		<dc:creator>Dmitri</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[f#]]></category>
		<category><![CDATA[fslex]]></category>
		<category><![CDATA[fsyacc]]></category>

		<guid isPermaLink="false">http://nesteruk.wordpress.com/?p=733</guid>
		<description><![CDATA[Все наверное так или иначе слышали про HTML Zen и про то, как он спасает при написании HTML. Но мало кому приходит в голову написать свою собственную реализацию, когда есть готовая на Python&#8217;е. На самом деле, собственная реализация позволяет добавлять собственные конструкты, нужные только вам. Давайте попробуем реализовать базовые конструкты HTML Zen. Для этого мы [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=733&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Все наверное так или иначе слышали про HTML Zen и про то, как он спасает при написании HTML. Но мало кому приходит в голову написать свою собственную реализацию, когда есть готовая на Python&rsquo;е. На самом деле, собственная реализация позволяет добавлять собственные конструкты, нужные только вам.</p>
<p>Давайте попробуем реализовать базовые конструкты HTML Zen. Для этого мы воспользуемся языком F#, библиотекой тестирования <a href="http://fsunit.codeplex.com/" title="http://fsunit.codeplex.com/">FSunit</a>. Будем писать программу в стиле TDD. Я пожалуй назову ее FSharpZen, так интересней.</p>
<p>Исходный код можно найти тут: <a href="https://bitbucket.org/nesteruk/fsharpzen">https://bitbucket.org/nesteruk/fsharpzen</a></p>
<h3>Базовые случаи</h3>
<p>Весь HTML Zen &ndash; это набор трансформаций. Например, на входе <code>a</code>, а на выходе <code>&lt;a href="|"&gt;&lt;/a&gt;</code>, где <code>|</code> обозначает финальное положение каретки. Следовательно, можно как минимум написать следующий тест:</p>
<p><pre class="brush: fsharp;">
[&lt;TestFixture&gt;]
type ``Basic transformation tests`` ()=
  [&lt;Test&gt;] member test.
    ``Empty input should yield empty output`` () =
      Zen.html &quot;&quot; |&gt; should equal &quot;|&quot;
</pre>
<p>Это счастье совсем не компилируется, поэтому нам придется как минимум предоставить хоть какую-то реализацию, например:</p>
<p><pre class="brush: fsharp;">
module FSharpZen.Zen
let html input = &quot;&quot;
</pre>
<p>В результате, конечно, получим &ldquo;красный&rdquo; тест:</p>
<p><pre class="brush: fsharp;">
Expected string length 1 but was 0. Strings differ at index 0.
Expected: &quot;|&quot;
But was:  &lt;string.Empty&gt;
-----------^
</pre>
<p>Ну а дальше мы &ldquo;жульничаем&rdquo; и наш тест проходит. Не забываем сказать спасибо Решарперу, который поддерживает F#-тесты out-of-the-box.</p>
<h3>Пустые тэги</h3>
<p>Дефолтное поведение HTML Zen такое, что при написании, скажем, буквы <code>z</code> разворачивается шаблон <code>&lt;z&gt;|&lt;/z&gt;</code>. С другой стороны, <code>hr</code> определенно превращается в <code>&lt;hr/&gt;|</code>, а <code>img</code> &ndash; в <code>&lt;img src="|"&gt;&lt;/img&gt;</code>.</p>
<p><pre class="brush: fsharp;">
[&lt;Test&gt;] member test.
  ``Simple entity should be in a closed tag`` () =
    Zen.html &quot;c&quot; |&gt; should equal &quot;&lt;c&gt;|&lt;/c&gt;&quot;
[&lt;Test&gt;] member test.
  ``Self-closing entity should yield a self-closing tag`` () =
    Zen.html &quot;br&quot; |&gt; should equal &quot;&lt;br/&gt;|&quot;
[&lt;Test&gt;] member test.
  ``Entity should have appropriate attributes generated and placeholder set accordingly`` () =
    Zen.html &quot;a&quot; |&gt; should equal &quot;&lt;a href=\&quot;|\&quot; title=\&quot;\&quot;&gt;&lt;/a&gt;&quot;
</pre>
<p>Теперь у нас есть 3 use-case&rsquo;а: просто тэги, тэги которые не нуждаются в закрывающем тэге, и тэги которые имеют сложную начинку. Второй и третий случай &ndash; специальные, поэтому мы можем попробовать их выделить&hellip;</p>
<p><pre class="brush: fsharp;">
let singularEntities = [&quot;hr&quot;; &quot;br&quot;; &quot;img&quot;]
let neededAttributes = [
                         &quot;a&quot;, [&quot;href&quot;, &quot;title&quot;];
                         &quot;img&quot;, [&quot;src&quot;, &quot;alt&quot;]
                       ]
</pre>
<p>TDD по наивности своей рекоммендует &ldquo;самое простое работающее решение&rdquo; которое в нашем случае выглядит примерно вот так:</p>
<p><pre class="brush: fsharp;">
let html input =
  if input |&gt; String.IsNullOrEmpty then &quot;|&quot;
  else
    let sb = StringBuilder()
    sb.Append (&quot;&lt;&quot; + input) |&gt; ignore
    for a in neededAttributes do
      match a with
      | i, attributes when i = input -&gt;
        let firstAtt = List.head attributes
        sb.Append(&quot; &quot; + firstAtt + &quot;=\&quot;|\&quot;&quot;) |&gt; ignore
        attributes |&gt; List.tail 
                   |&gt; List.iter (fun f -&gt; sb.Append(&quot; &quot; + f + &quot;=\&quot;\&quot;&quot;) |&gt; ignore)
      | _ -&gt; ()
    // see if this is a self-closing tag
    let isSelfClosing = List.exists (fun f -&gt; f = input) singularEntities
    let pipe = if sb.ToString().Contains(&quot;|&quot;) then &quot;&quot; else &quot;|&quot;
    sb.Append(if isSelfClosing then &quot;/&gt;&quot; + pipe else &quot;&gt;&quot; + pipe + &quot;&lt;/&quot; + input + &quot;&gt;&quot;) |&gt; ignore
    sb.ToString()
</pre>
<p>До элегантности тут как до Китая. Вывода напрашивается два:</p>
<ul>
<li>Нужны какие-то структуры для того чтобы отражать структуру тэгов, особенно когда будут вложенные, с аттрибутами, и т.д.</li>
<li>Нужно учиться лучше парсить строки. То, что мы делаем выше &ndash; &ldquo;не по паттернам&rdquo;.</li>
</ul>
<h3>FsLex и FsYacc</h3>
<p>Лучший способ парсить &ndash; это взять что-то уже готовое (хотя до меня это иногда туго доходит). Поэтому для парсинга наших магических строк мы возьмем <a href="http://fsharppowerpack.codeplex.com/" title="http://fsharppowerpack.codeplex.com/">F# Power Pack</a>, и скачаем через VS Extension Manager шаблон проекта под названием <a href="http://visualstudiogallery.msdn.microsoft.com/a075ff98-7e6f-47ce-a23c-838c1e488046/" title="http://visualstudiogallery.msdn.microsoft.com/a075ff98-7e6f-47ce-a23c-838c1e488046/">F# Parser Language Starter</a>. Это позволит нам получить пример, в котором есть три важных файла, а именно:</p>
<ul>
<li><code>Ast.fs</code> &mdash; определение синтактического дерева. В нашем случае оно достаточно простое.</li>
<li><code>Lexer.fsl</code> &mdash; определение &ldquo;лексем&rdquo;, т.е. различных кусков строки, которые формируют выражение HTML Zen.</li>
<li><code>Parser.fsy</code> &mdash; собственно парсер, который определяет то, как лексемы становятся чем-то осмысленным.</li>
</ul>
<p>В сгенерированном примере получается калькулятор<sup><a href="#Reference1" title="Кстати, пример кривой &ndash; попробуйте например ввести 2-2 и у вас парсер моментально накроется &ndash; а все из-за попытки взять минус перед 2кой как префикс-оператор. :)" name="BackReference1">1</a></sup>, нам же остается только поменять его чтобы начали поддерживаться наши собственные структуры.</p>
<h3>AST</h3>
<p>Итак, для нашего магического синтаксиса нам нужно определить тот набор элементов дерева, который мы фактически готовы подерживать. Это включает в себя как минимум следующие конструкции<sup><a href="#Reference2" title="Тут я немного отклонился от &ldquo;кошерного&rdquo; HTML Zen который, на мой взгляд, достаточно избыточен. В частности, я заменил &gt; на + а &ldquo;оригинальный&rdquo; + попросту проигнорировал." name="BackReference2">2</a></sup>:</p>
<ul>
<li>Идентификаторы, такие как <code>div</code>, <code>href</code> и так далее. Они парсятся &ldquo;как есть&rdquo;.</li>
<li>Классы, т.е. при при написании <code>a.b</code> мы получаем <code>&lt;a href="|" class="b"&gt;&lt;/a&gt;</code>.</li>
<li>Идентификаторы, т.е. <code>a#b</code> ставовится <code>&lt;a id="b" href="|"&gt;&lt;/a&gt;</code>.</li>
<li>Множители, т.е. <code>hr*2</code> дает нам <code>&lt;hr/&gt;&lt;hr/&gt;</code>. Между тэгами, между прочим, фигурирует <code>\n</code> &ndash; это вынужденная мера для таких ситуаций как <code>ol&gt;li*2</code>.</li>
<li>Вложенные тэги &ndash; например, <code>ol+li</code> становится <code>&lt;ol&gt;&lt;li&gt;|&lt;/li&gt;&lt;/ol&gt;</code>, с соответствующими переносами и отступами.</li>
</ul>
<p>Теперь попробуем перевисти все эти идеи на структуры F#. У меня получилось примерно следующее:</p>
<p><pre class="brush: fsharp;">
type ZenExpression =
  | ZenExpression of Expression list
and Expression =
  | Expression of Identifier * Qualifier list
and Identifier =
  | Identifier of string
and Qualifier =
  | ClassQualifier of string
  | IdentityQualifier of string
  | CardinalityQualifier of int
</pre>
<p>Строго говоря, то, что вы видите выше &ndash; уродство. С этим очень сложно работать. К тому же, нет никаких ограничений на количество идентификатов или показателей размерности (а ведь элемент может иметь только один квалификатор каждого типа). Ну да ладно, эту проблему мы разрулим позже.</p>
<h3>Лексер</h3>
<p>Лексер у нас будет простенький. Во-первых, нужно определить регулярные выражения для всех полезных конструктов:</p>
<p><pre class="brush: fsharp;">
let digit = ['0'-'9']
let letter = ['a'-'z'] | ['A'-'Z']
let letterOrDigit = letter | digit
let whitespace = [' ' '\t' ]
let newline = ('\n' | '\r' '\n')
</pre>
<p>Ну а теперь, нужно определить какие конструкты будут производится из регулярных выражений. Нам нужны:</p>
<ul>
<li>Строки (возможно с числами) для идентификаторов и квалификаторов.</li>
<li>Операторы для разделения квалификаторов (<code>.</code>, <code>#</code>, <code>+</code> и <code>*</code>)</li>
<li>Числа для квалификатора размерности</li>
</ul>
<p>Следовательно, получаем следующее определение:</p>
<p><pre class="brush: fsharp;">
rule tokenize = parse
| whitespace	   { tokenize lexbuf }
| newline        { tokenize lexbuf }
| letterOrDigit+ { STRING(lexeme lexbuf) }
| digit+         { INT32(Int32.Parse(lexeme lexbuf)) }
// Operators
| &quot;+&quot;            { PLUS }
| &quot;#&quot;            { HASH }
| &quot;*&quot;            { TIMES }
| &quot;.&quot;            { DOT }
// EOF
| eof   { EOF }
</pre><br />
<h3>Парсер</h3>
<p>Определив лексемы, нам нужно научиться парсить их и превращать их комбинации в элементы AST. Для этого пойдет следующее определение:</p>
<p><pre class="brush: fsharp;">
start: exprs { ZenExpression($1) }
exprs:
  | exp PLUS exprs EOF { Expression($1) :: $3 }
  | exp EOF { [Expression($1)] }
exp:
  | STRING quals { (Identifier($1), $2) }
  | STRING { (Identifier($1), []) }
quals:
  | qual quals { $1 :: $2 }
  | qual { [$1] }
qual:
  | DOT STRING  { ClassQualifier($2) }
  | HASH STRING { IdentityQualifier($2) }
  | TIMES INT32 { CardinalityQualifier($2) }
</pre>
<p>Помимо того, что списки чего бы то ни было создаются рекурсивно, тут нет ничего необычного.</p>
<h3>Принтер</h3>
<p>Ну а теперь самое сложное. Помните я сказал, что та структура в которую мы парсим &ndash; убогая и избыточная? Так вот, сейчас вы увидите насколько неудобно такую штуку печатать.</p>
<p>Во-первых, код забрасывается кучей вспомогательных функций для поиска квалификаторов определенного типа. Например:</p>
<p><pre class="brush: fsharp;">
let rec private getId qualifierList =
  match qualifierList with
  | IdentityQualifier(id) :: t -&gt; Some(id)
  | h :: t -&gt; getId t
  | [] -&gt; None
</pre>
<p>Потом, приходится (не знаю в какой уже раз) делать вменяемый билдер для кода:</p>
<p><pre class="brush: fsharp;">
type CodeBuilder() =
  let sb = StringBuilder()
  let mutable indent = 0;
  member this.GetIndent() =
    System.String.Empty.PadRight(indent * 2)
  member this.AppendWithIndent (text:string) = 
    sb.Append(this.GetIndent()).Append(text) |&gt; ignore
  member this.Append (text:string) = 
    sb.Append(text) |&gt; ignore
  member this.NewLine() = sb.AppendLine() |&gt; ignore
  member this.Indent() = indent &lt;- indent + 1
  member this.Unindent() = indent &lt;- indent - 1
  override this.ToString() = sb.ToString()
</pre>
<p>Потом нужен поистине гигантский кусок кода для того чтобы распарсить все кейсы &ndash; конструкция весьма громоздкая:</p>
<p><pre class="brush: fsharp;">
let rec private print expressions (builder:CodeBuilder) =
  match expressions with
  | h :: t -&gt;
    builder.AppendWithIndent &quot;&lt;&quot;
    match h with
    | Expression(name, qual) -&gt;
      builder.Append name
      // id it if has any
      match getId qual with
      | Some(id) -&gt; builder.Append(&quot; id=\&quot;&quot; + id + &quot;\&quot;&quot;)
      | _ -&gt; ()
      // needed attributes
      let needed = getNeededAttributes name
      needed |&gt; List.iter(fun n -&gt; builder.Append(&quot; &quot; + n + &quot;=\&quot;|\&quot;&quot;))
      // classes
      let classes = getClasses qual
      if classes &lt;&gt; List.empty then
        builder.Append(&quot; class=\&quot;&quot;)
        builder.Append(System.String.Join(&quot; &quot;, classes))
        builder.Append(&quot;\&quot;&quot;)
      // if self-closing, then close it
      let selfClosing = singularEntities |&gt; List.exists(fun f -&gt; f = name)
      if selfClosing then builder.Append &quot;/&gt;|&quot;
      else builder.Append &quot;&gt;&quot;
      // if there's more stuff in there, add it
      if t &lt;&gt; List.empty then
        builder.Indent()
        builder.NewLine()
        print t builder
        builder.NewLine()
        builder.Unindent()
        
      if not selfClosing then
        builder.Append(&quot;|&lt;/&quot; + name + &quot;&gt;&quot;)
  | _ -&gt; 
    builder.Append &quot;|&quot;
</pre>
<p>А вот собственно как все запускается:</p>
<p><pre class="brush: fsharp;">
let html input = 
  let lexbuf = LexBuffer&lt;char&gt;.FromString(input)
  let parsed = Parser.start Lexer.tokenize lexbuf
  let builder = new CodeBuilder()
  match parsed with
  | ZenExpression(items) -&gt; print items builder
  let pre = builder.ToString() |&gt; Seq.toList
  let rec clean input metBar =
    match input with
    | '|' :: t when not metBar -&gt; '|' :: clean t true
    | '|' :: t when metBar -&gt; clean t true
    | h :: t -&gt; h :: clean t metBar
    | [] -&gt; []
  let lst = clean pre false
  System.String(lst |&gt; List.toArray)
</pre>
<p>Излишние телодвижения у нас от того, что мы в принтере генерируем избыточное число кареток. Нужно удалить все кроме первой, а более элегантного способа я не знаю (хотя не исключаю что он существует).</p>
<h3>Заключение</h3>
<p>В очередной раз у меня сомнения. Количество телодвижений для того чтобы реализовать парсер примерно в 10 раз больше чем когда я все это писал &ldquo;в лоб&rdquo; на C#. FxLex и FsYacc обладают практически нулевой диагностичностью, никакого IntelliSense нет, такое впечатление что блуждаешь в потемках. Что касается принтера, опять же, структура на которую все было &ldquo;замэплено&rdquo; очень неудобна; намного быстрее было бы использовать ООП. Плюшки FP вроде паттерн-матчинга и прочего тут совсем не амортизировались.</p>
<h3>Заметки</h3>
<ol>
<li><a name="Reference1"></a><a href="#BackReference1" title="Back to text">&uarr;</a> Кстати, пример кривой &ndash; попробуйте например ввести <code>2-2</code> и у вас парсер моментально накроется &ndash; а все из-за попытки взять минус перед 2кой как префикс-оператор. :)</li>
<li><a name="Reference2"></a><a href="#BackReference2" title="Back to text">&uarr;</a> Тут я немного отклонился от &ldquo;кошерного&rdquo; HTML Zen который, на мой взгляд, достаточно избыточен. В частности, я заменил <code>&gt;</code> на <code>+</code> а &ldquo;оригинальный&rdquo; <code>+</code> попросту проигнорировал.</li>
</ol>
<br />Filed under: <a href='http://nesteruk.wordpress.com/category/net/'>.NET</a>, <a href='http://nesteruk.wordpress.com/category/net/f/'>f#</a> Tagged: <a href='http://nesteruk.wordpress.com/tag/fslex/'>fslex</a>, <a href='http://nesteruk.wordpress.com/tag/fsyacc/'>fsyacc</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/nesteruk.wordpress.com/733/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/nesteruk.wordpress.com/733/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/nesteruk.wordpress.com/733/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/nesteruk.wordpress.com/733/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/nesteruk.wordpress.com/733/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/nesteruk.wordpress.com/733/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/nesteruk.wordpress.com/733/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/nesteruk.wordpress.com/733/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/nesteruk.wordpress.com/733/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/nesteruk.wordpress.com/733/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/nesteruk.wordpress.com/733/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/nesteruk.wordpress.com/733/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/nesteruk.wordpress.com/733/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/nesteruk.wordpress.com/733/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=733&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://nesteruk.wordpress.com/2011/03/03/parsing-htmlzen-fsharp-fslex-fsyacc/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f01cbd2238e2a78e4c43fa596f51d6a1?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=G" medium="image">
			<media:title type="html">nesteruk</media:title>
		</media:content>
	</item>
		<item>
		<title>Уравнения на F# проще чем на C#?</title>
		<link>http://nesteruk.wordpress.com/2011/02/05/are-fsharp-equations-easier-than-csharp/</link>
		<comments>http://nesteruk.wordpress.com/2011/02/05/are-fsharp-equations-easier-than-csharp/#comments</comments>
		<pubDate>Sat, 05 Feb 2011 07:52:32 +0000</pubDate>
		<dc:creator>Dmitri</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[f#]]></category>

		<guid isPermaLink="false">http://nesteruk.wordpress.com/?p=718</guid>
		<description><![CDATA[На недавней встрече, посвещенной языку F#, я показал как якобы «элегантно» можно описывать математические функции на F#. А сейчас сел и задумался &#8211; так ли это на самом деле? Давайте разберемся. Квадратное уравнение Начнем с примера, приведенного мною на встрече &#8211; решения квадратного уравнения. Казалось бы &#8211; что может быть проще? Сразу можно сделать несколько [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=718&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>На <a href="http://spbalt.net/Home/Meetings/25" title="http://spbalt.net/Home/Meetings/25">недавней встрече</a>, посвещенной языку F#, я показал как якобы «элегантно» можно описывать математические функции на F#. А сейчас сел и задумался &ndash; так ли это на самом деле? Давайте разберемся.</p>
<h3>Квадратное уравнение</h3>
<p>Начнем с примера, приведенного мною на встрече &ndash; решения квадратного уравнения. Казалось бы &ndash; что может быть проще?</p>
<p align="center"><img src="http://nesteruk.org/pix/0/4d155c02-150c-48b9-a862-c6ac1cfb5d6b.png" alt="" style="border:0;" align="center" class="aligncenter" /></p>
<p>Сразу можно сделать несколько сравнений C# и F# для этого примера:</p>
<ul>
<li>Возвращаются 2 значения, а следовательно F# выигрывает за счет того что не надо писать <code>Tuple.Create</code>, а тому кто эти значения получит не надо использовать абсолютно ничего не значащие свойства <code>Item1</code> и <code>Item2</code></li>
<li>Математические binding&rsquo;и у F# намного «ближе» чем у C#, а следовательно в этом случае (да и в общем), ваш код на F# не будет испачкан явным вызовом статических функций вроде <code>Math.Sqrt</code>. Меня всегда это бесило в C# т.к. сложные вычисления становится еще сложнее читать.</li>
<li>Ни C# ни F# не умеют работать с оператором ±. Но если в C# нам придется тупо вызывать вычисления дважды, в F# мы можем просто определить функцию конечного вычисления, а потом передать в нее операторы <code>(+)</code> и <code>(-)</code>.</li>
</ul>
<p>В результате получим следующее сравнение:</p>
<p>
  <strong>C#</strong>
</p>
<p><pre class="brush: csharp;">
public Tuple&lt;double,double&gt; SolveQuadraticEquation(double a, double b, double c)
{
  double discRoot = Math.Sqrt(b*b-4.0*a*c);
  double x1 = (-b + discRoot) / (2 * a);
  double x2 = (-b - discRoot) / (2 * a);
  return Tuple.Create(x1, x2);
}
</pre>
<p>
  <strong>F#</strong>
</p>
<p><pre class="brush: fsharp;">
let solveQuadratic a b c =
  let disc = b * b - 4.0 * a *c
  let calc op = (op (-b) (sqrt disc)) / (2.0*a)
  (calc (+), calc(-))
</pre>
<p>Пока что разница небольшая &ndash; в одну строчку, если не считать фигурных скобок. А спорим я могу сократить эту разницу практически до нуля? Вот, пожалуйста:</p>
<p>
  <strong>C#</strong>
</p>
<p><pre class="brush: csharp;">
public Tuple&lt;double, double&gt; SolveQuadraticEquation(double a, double b, double c)
{
  double q = -0.5 * (b + Math.Sign(b) * Math.Sqrt(b * b - 4 * a * c));
  return Tuple.Create(q / a, c / q);
}
</pre>
<p>
  <strong>F#</strong>
</p>
<p><pre class="brush: fsharp;">
let solveQuadraticEquation a b c =
  let q = -0.5 * (b + (sign b |&gt; float) * sqrt(b * b - 4.0 * a * c))
  (q / a, c / q) //           ^^^^^^^^ WTF?!?
</pre>
<p>Пример на F# должен вызвать у вас возмущение &ndash; с какой это радости результат <code>(sign b)</code> должен быть приведен к типу <code>float</code>?<sup><a href="#Reference1" title="Напоминаю, что float в F# &ndash; это double в C#. А C#-ный float в F# называется float32." name="BackReference1">1</a></sup> А дело все в непродуманности API, согласно которому <code>sign(x)</code> &ndash; это всегда <code>int</code> (могли бы сделать тем же типом что и аргумент), а также крайне жесткой системе типов в которой <code>float + int * float</code> не воспринимается и никак не вычисляется.</p>
<p>Такое странное поведение F# унаследовано из OCaml и требуется для правильного вывода типов. Если вам действительно интересны причины &ndash; можно <a href="http://cs.hubfs.net/forums/thread/3391.aspx" title="http://cs.hubfs.net/forums/thread/3391.aspx">почитать тут</a>. Мне же это лишь палки в колеса. Да, и я знаю что можно было бы написать <code>sign(float b)</code> или что-то в этом роде. Тут как бы без разницы, все равно «не очень».</p>
<h3>Комплексные решения</h3>
<p>Ну да ладно, а вот вопрос &ndash; что будет если дискриминанта (&Delta;) меньше нуля? С одной стороны, и C# и F# поддерживает комплексные типы данных (см. System.Numerics). Если сделать вид что мы все будем передавать как комплексные числа (так проще?), то получим вот такой вот код:</p>
<p>
  <strong>C#</strong>
</p>
<p><pre class="brush: csharp;">
public Tuple&lt;Complex, Complex&gt; SolveQuadraticEquation3(double a, double b, double c)
{
  double det = b * b - 4 * a * c;
  double absRoot = Math.Sqrt(Math.Abs(det));
  Complex root = det &lt; 0 ? new Complex(0, absRoot) : new Complex(absRoot, 0);
  Complex q = -0.5 * (b + Math.Sign(b) * root);
  return Tuple.Create(q / a, c / q);
}
</pre>
<p><strong>F#</strong></p>
<p><pre class="brush: fsharp;">
let solveQuadratic3 a b c =
  let complex v = Complex(v, 0.0)
  let det = b * b - 4.0 * a * c
  let absRoot = sqrt(det |&gt; abs)
  let root = if det &lt; 0.0 then Complex(0.0, absRoot)
                          else Complex(absRoot, 0.0)
  let q = -(complex 0.5) * ((complex b) + (sign b |&gt; float |&gt; complex) * root)
  (q / complex(a), complex(c) / q)
</pre>
<p>Эээ, ну что я могу сказать? F# определенно превносит массу головной боли. Проблема тут как раз в том, что в C# нам доступны операторы автоконверсии вроде <code>float</code>&rarr;<code>Complex</code>, но в F# они попросту не работают! Поэтому вы можете либо определить их как функции (благо у них есть имя <code>op_Implicit</code>), либо же просто вызвать конструктор как делаю я.<sup><a href="#Reference2" title="Тут еще хочется заметить что if который выбирает какой Complex создавать как бы не нужно &mdash; можно было вместо этого написать Complex.Sqrt(new Complex(b*b-4*a*c, 0.0)). Проблема только в том что в результате этого вычисления Real-значение будет чуточку отличаться от нуля. Например sqrt(Complex(-4.0, 0.0)) равен (1.22460635382238E-16, 2)." name="BackReference2">2</a></sup></p>
<h3>Заключение</h3>
<p>То ли я чего-то недопонимаю, то ли F# действительно неудобен для работы с математикой? Ведь если нужно каждый <code>int</code> кастовать во <code>float</code>, а каждый <code>float</code> в <code>Complex</code>, и так далее, то это же сколько лишнего, никому не нужного и мешающего восприятию кода надо написать?</p>
<p>Критика welcome! А то может я что не так написал?</p>
<h3>Заметки</h3>
<ol>
<li><a name="Reference1"></a><a href="#BackReference1" title="Back to text">&uarr;</a> Напоминаю, что <code>float</code> в F# &ndash; это <code>double</code> в C#. А C#-ный <code>float</code> в F# называется <code>float32</code>.</li>
<li><a name="Reference2"></a><a href="#BackReference2" title="Back to text">&uarr;</a> Тут еще хочется заметить что <code>if</code> который выбирает какой <code>Complex</code> создавать как бы не нужно &mdash; можно было вместо этого написать <code>Complex.Sqrt(new Complex(b*b-4*a*c, 0.0))</code>. Проблема только в том что в результате этого вычисления Real-значение будет <em><font face="Trebuchet MS" />чуточку</font></em> отличаться от нуля. Например <code>sqrt(Complex(-4.0, 0.0))</code> равен <code>(1.22460635382238E-16, 2)</code>.</li>
</ol>
<br />Filed under: <a href='http://nesteruk.wordpress.com/category/net/'>.NET</a>, <a href='http://nesteruk.wordpress.com/category/c/'>C#</a>, <a href='http://nesteruk.wordpress.com/category/net/f/'>f#</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/nesteruk.wordpress.com/718/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/nesteruk.wordpress.com/718/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/nesteruk.wordpress.com/718/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/nesteruk.wordpress.com/718/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/nesteruk.wordpress.com/718/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/nesteruk.wordpress.com/718/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/nesteruk.wordpress.com/718/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/nesteruk.wordpress.com/718/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/nesteruk.wordpress.com/718/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/nesteruk.wordpress.com/718/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/nesteruk.wordpress.com/718/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/nesteruk.wordpress.com/718/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/nesteruk.wordpress.com/718/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/nesteruk.wordpress.com/718/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=718&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://nesteruk.wordpress.com/2011/02/05/are-fsharp-equations-easier-than-csharp/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f01cbd2238e2a78e4c43fa596f51d6a1?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=G" medium="image">
			<media:title type="html">nesteruk</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/0/4d155c02-150c-48b9-a862-c6ac1cfb5d6b.png" medium="image" />
	</item>
		<item>
		<title>Коротко про WebSharper и сайтлеты</title>
		<link>http://nesteruk.wordpress.com/2011/01/19/on-websharper-and-sitelets/</link>
		<comments>http://nesteruk.wordpress.com/2011/01/19/on-websharper-and-sitelets/#comments</comments>
		<pubDate>Wed, 19 Jan 2011 20:53:19 +0000</pubDate>
		<dc:creator>Dmitri</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[f#]]></category>
		<category><![CDATA[sitelet]]></category>
		<category><![CDATA[websharper]]></category>

		<guid isPermaLink="false">http://nesteruk.wordpress.com/?p=704</guid>
		<description><![CDATA[Этот пост &#8211; про WebSharper в целом и про сайтлеты в частности. Как я уже писал, WebSharper &#8211; это идея создания сайтов полностью на языке F#. У этой идеи есть несколько модельных реализаций, а точнее три &#8211; через Asp.Net, Asp.Net MVC и так называемые сайтлеты &#8220;sitelets&#8221;. Концепция Меня во всей этой концепции интересуют именно сайтлеты. [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=704&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><img src="http://nesteruk.org/pix/0/1ac970e1-40cf-4dc7-ac88-adc39a02a8e5.png" class="alignright" alt="" style="border:0;" />Этот пост &ndash; про <a href="http://websharper.com" title="http://websharper.com">WebSharper</a> в целом и про сайтлеты в частности. Как я уже писал, WebSharper &ndash; это идея создания сайтов полностью на языке F#. У этой идеи есть несколько модельных реализаций, а точнее три &ndash; через Asp.Net, Asp.Net MVC и так называемые сайтлеты &ldquo;sitelets&rdquo;.</p>
<h3>Концепция</h3>
<p>Меня во всей этой концепции интересуют именно сайтлеты. В отличии от использования Asp.Net-конструктов (пусть и на F#), сайтлеты &ndash; наиболее &ldquo;трушная&rdquo; концепция, в которой сгенерированный код будет <strong>полностью отвязан</strong> от Asp.Net и, впоследствии, от IIS.</p>
<p>Теперь о том <em><font face="Trebuchet MS" />как</font></em> это делается. На самом деле, основной концепцией является конверсия F# в JavaScript/CSS/HTML на этапе компиляции. Делается это за счет аттрибута под названием <code>ReflectedDefinitionAttribute</code> который, имея alias под названием <code>JavaScriptAttribute</code>, используется перед методами, которые нужно сконвертировать в JavaScript.</p>
<p>Что этот аттрибут делает? Он заставляет компилятор F# сохранить логические конструкты метода вместо того чтобы просто его компилировать. А тут включается постпроцессинг WebSharper&rsquo;а, который берет это &ldquo;отраженное определение&rdquo; и, на его основе, формирует некий промежуточный слой JavaScript. Промежуточный слой нужен потому, что мэпить F# на JavaScript не так-то просто.</p>
<p>Простой код который вы напишете вот так</p>
<p><pre class="brush: fsharp;">
[&lt;JavaScript&gt;]
let rec Fact n =
  match n with
  | n when n &lt; 2 -&gt; 1
  | n -&gt; n * Fact1 (n - 1)
</pre>
<p>может быть в последствии вызван со страницы напрямую &ndash; ведь все остальное, что есть на странице тоже определено через F#, конвертированный в JS и HTML.</p>
<h3>Вызовы серверных методов</h3>
<p>Мне очень понравилось что механизм вызова методов очень прозрачный. Метод помеченный как <code>[&lt;JavaScript&gt;]</code> вызывается как JS прямо у клиента, в то время как метод помеченный как <code>[&lt;Rpc&gt;]</code> хостится на сервере (как это будет сделано в полноценных сайтлетах &ndash; непонятно), и соответственно его вызов со страницы дергает сервер.</p>
<p>На данный момент механизм поведения Rpc-методов такой: если метод возвращает какое-то значение, то вызов является блокирующим. Если метод возвращает <code>unit</code> &ndash; его вызов считается асинхронным и тем самым, если вы повесили на него брейкпоинт (да, это можно делать), вы можете остановиться в нем не тогда, когда ожидаете.</p>
<h3>Формирование контента</h3>
<p>ВебШарпер не отказывается от идеи шаблонов! По крайней мере сайтлеты на текущем этапе предоставляют возможность писать шаблоны в HTML, который потом компилируется&hellip; в F#! Что намного более извращенно чем то, что делает Asp.Net MVC, например. Но мы сейчас не об этом.</p>
<p>Дело в том, что сам по себе WebSharper предоставляет возможность формировать HTML путем использования HTML-подобных конструктов в F#. Механизм, с помощью которого организовано создание HTML весьма забавно. В модуле <code>IntelliFactory.Html.Tags</code> есть определения для большого количества тэгов, например <code>H1</code>. Каждый тэг &ndash; это функция, которая сама по себе берет список, состоящий из нескольких &ldquo;нодов&rdquo;, которые могут формировать как аттрибуты данного тэга, так и его начинку. Например, чтобы сделать заголовок с текстом, мы пишем следующее:</p>
<p><pre class="brush: fsharp;">
H1 [Text &quot;Title&quot;]
</pre>
<p>В примере выше, метод <code>Text</code> определяет, что его аргумент &ndash; это действительно текст. Вместо него, мы бы могли например использовать <code>Attr</code> чтобы указать что это аттрибут.</p>
<p>Теперь насчет композиции. Композиция &ndash; это <em><font face="Trebuchet MS" />центральная</font></em> особенность ВебШарпера, которая очень хорошо выделяет его по сравнению с тем же Asp.Net MVC. В данном случае, для вкладывания HTML-элементов друг в друга, мы можем использовать оператор <code>-&lt;</code>. Например:</p>
<p><pre class="brush: fsharp;">
Div [Attr.Style &quot;padding: 5px;&quot;] -&lt; [
  P [Text &quot;Hello&quot;]
]
</pre><br />
<h3>События</h3>
<p>Обработка событий в F# делается путем еще одного полезного оператора <code>|&gt;!</code>. Этот оператор делает подписку на событие, и одновременно возвращает созданный элемент, что обязательно в условиях механики работы WebSharper. Вот небольшой пример:</p>
<p><pre class="brush: fsharp;">
let update () =
  // обработка события
 
let input = Input [Attr.Type &quot;Text&quot;]
Form [
  label &quot;First Name: &quot;
  input |&gt;! OnKeyUp (fun _ _ -&gt; update ())
]
</pre>
<p>Опять же, интересно тут то, что не важно, где располагается метод <code>update()</code> &ndash; на сервере или на клиенте.</p>
<h3>Это вам не MVC!</h3>
<p>Сейчас устраивать гонения на MVC <a href="http://www.gotdotnet.ru/blogs/sanchez911/9266/" title="http://www.gotdotnet.ru/blogs/sanchez911/9266/">модно</a>, многие уходят на Rails (в т.ч. всем известный <a href="http://www.osherove.com/blog/2011/1/2/the-journey-begins-and-why-it-starts-with-ruby.html" title="http://www.osherove.com/blog/2011/1/2/the-journey-begins-and-why-it-starts-with-ruby.html">Рой Ошеров</a>), поэтому я решил тоже подлить масла в огонь хотя, по-хорошему, ВебШарпер является частью Asp.Net инфраструктуры. Но это пока сайтлеты полностью не дописаны. Так вот, понятное дело, что в функциональной парадигме, создание сайтов выглядит несколько по-другому.</p>
<p>Для начала, давайте познакомимся с концепцией &ldquo;формлетов&rdquo;. <a href="http://bugsquash.blogspot.com/2011/01/simple-implementation-of-formlets-in-f.html">Формлет</a> &ndash; это возможность описания того, какие данные нужно получать в форме и как пройдет валидация. Например:</p>
<p><pre class="brush: fsharp;">
[&lt;JavaScript&gt;]
let BasicInfoForm () : Formlet&lt;BasicInfo&gt; =
  Formlet.Yield (fun name age -&gt; { Name = name; Age = age })
  &lt;*&gt; Input &quot;Name&quot; &quot;Please enter your name&quot;
  &lt;*&gt; InputInt &quot;Age&quot; &quot;Please enter a valid age&quot;
</pre>
<p>Формлеты можно &ldquo;обрамлять&rdquo; (типичный паттерн Декоратор) в различные другие конструкты, функционально добавляя им кнопки Submit/Reset, настройки, и так далее. Формлеты можно инстанциировать у себя в коде, причем с ними можно делать вещи, которые в том же MVC делать очень сложно. Например, допустим у вас сайт где можно разместить резюме, и вы хотите чтобы человек мог внести данные о всех местах где он работал. Для этого, вы делаете формлет, который инкапсулирует все данные о работе (где, когда, итп.) а потом одной строчкой кода говорите &ldquo;их может быть несколько&rdquo;. И все! Композиционная структура сделает все сама за себя.</p>
<p>Так вот, о чем мы? После того как мы сделали несколько формлетов, их можно выстроить в цепочку, так что нажав Submit на одном, мы перейдем к другому. Эта &ldquo;концепция&rdquo; именуется flowlet&rsquo;ом (или flowlet&rsquo;ами). Выражается это следующим образом.</p>
<p>Во-первых, есть некий workflow под названием <code>Flowlet.Do</code> этот вокрфлоу умеет показывать формы в тот момент, когда они учавствуют по правую сторону <code>let!</code>, и сохранять их возвращенные значения (да-да, форма это функция, а ее return value &ndash; это то, что пользователь ввел). После этого, путем <code>return!</code> происходит &ldquo;типа возврат&rdquo; из этой цепочки, то есть мы можем, например, предоставить фунцию, которая получит результаты formlet&rsquo;ов и выведет из них полезные значения на экран.</p>
<p>На самом деле, механика всего этого чуть-чуть сложнее. Во-первых, конечным результатом выражения <code>Formlet.Do</code> все равно должен быть <code>Formlet</code>, то есть создав элемент, вам придется его конвертировать. Но это не сложно! А во-вторых, весь этот computational expression должен быть передан в функцию <code>Formlet.Flowlet</code> для того чтобы оркестровать такое феерическое действие.</p>
<p><pre class="brush: fsharp;">
Formlet.Do {
  let! w = welcomeForm
  let! c = consentForm
  return! Formlet.OfElement (proc w c)
}
|&gt; Formlet.Flowlet
</pre><br />
<h3>Представление</h3>
<p>На данный момент презентация сего фееричного действия организована в терминах Asp.Net&rsquo;ного <code>WebControl</code>, от которого наследует ВебШарперный контрол, давая нам возможность засунуть в него целиком наш pagelet. Pagelet &ndash; это еще одна абстракция в стиле &ldquo;нечто что может фигурировать на странице&rdquo;. Логика мне не ясна, но на данный момент pagelet&rsquo;ом являются текст, <code>Element</code> (это просто элемено HTML) или <code>Flowlet</code>.</p>
<p>Понятное дело, что формирование отдельного <code>WebControl</code>-а не означает создание странички целиком. И вот тут начинаются странности.</p>
<p>Дело в том, что для сайтлетов у нас генерируются 3 проекта, а именно:</p>
<ul>
<li>Проект Asp.Net, который содержит некоторые ресурсы (например CSS), но в будущем скорее всего не будет нужен вообще. По крайней мере, сейчас для сайтлетов он служит заглушкой со ссылкой на &ldquo;настоящий сайт&rdquo;.</li>
<li>Проект сожержащий сайтлет.</li>
<li>Проект с шаблонами.</li>
</ul>
<p>Проект с шаблонами &ndash; это возможность делать, фактически, master-странички со вставками, определеными в F#. Вы уже догадались почему нужно конвертировать HTML в F#? Поясню: в основном проекте, у нас появляется возможность <em><font face="Trebuchet MS" />строготипизированно</font></em> определить, что мы помещаем в различные секции шаблона. Обычно туда помещяется либо наш F#-созданный HTML, либо&hellip; наш <code>WebControl</code>, конечно же!</p>
<p>За этим всем стоит разве что само определение сайта. Там собраны все шаблоны, и еще есть некий plumbing для того чтобы все работало.</p>
<h3>Размышления</h3>
<p>Для меня, идеальным кажется сайт, который существует целиком на клиенте, а с сервером общается только через REST или более продвинутую парадигму <em><font face="Trebuchet MS" />a la</font></em> NetKernel. Поэтому в случае с ВебШарпером (надеюсь мне авторы простят русификацию названия), то чего хочу видеть я &ndash; это полную отвязку от Asp.Net и IIS. Я точно не знаю когда это будет, но коллеги подсказывают мне, что релиз WebSharper 2.0 будет отвязан от Asp.Net, в то время как полноценная поддержка сайтлетов с отвязкой от IIS будет в последующем релизе. Главное, что для перехода на Linux (а это основная цель) не надо будет ничего переписывать. По крайней мере я на это надеюсь.</p>
<p>Теперь что касается <em><font face="Trebuchet MS" />юзабельности</font></em> сего фреймворка. Понятное дело что вкуривать сложную DSL и писать на F# &ndash; занятие не для слабонервных, но лично меня технические трудности не очень беспокоят. Композициональность, если так можно выразиться, фреймворка впечатляет, ровно как и его возможность быстро оборачивать такие JS библиотеки как JQueryUI, YUI или даже RxJS! Это означает что в F# можно получить унифицированную, строготипизированную модель, которая будет сама подсказывать как использовать тот или иной фреймворк и упрощать работу с ним.</p>
<h3>Анонсы</h3>
<p>Коротко о главном. Во-первых, Петербургская Группа Alt.Net планирует провести 3х-часовую встречу посвещенную F#. Максим Моисеев и я будем рассказывать про все, начиная с основных понятий и заканчивая спекуляциями на тему type provider&rsquo;ов. Встреча пройдет 3го Февраля в офисах Exigen, и в ближайшее время мы вывесим анонс на сайтах Ineta и Spbalt.net.</p>
<p>Во-вторых, на выходных периодически происходят неформальные встречи участников сообщества. Встречи происходят в моем офисе (территориально 3 мин. от м. Автово), так что если вы живете в Питере и у вас есть желание убить выходной копаясь в F# (или еще какой-нть интересной фигне) с единомышленниками, напишите мне и мы обсудим. Писать лучше по скайпу &mdash; <code>dmitri.nesteruk</code>.</p>
<p>На этом все. Comments welcome!</p>
<br />Filed under: <a href='http://nesteruk.wordpress.com/category/net/'>.NET</a>, <a href='http://nesteruk.wordpress.com/category/net/f/'>f#</a> Tagged: <a href='http://nesteruk.wordpress.com/tag/sitelet/'>sitelet</a>, <a href='http://nesteruk.wordpress.com/tag/websharper/'>websharper</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/nesteruk.wordpress.com/704/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/nesteruk.wordpress.com/704/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/nesteruk.wordpress.com/704/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/nesteruk.wordpress.com/704/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/nesteruk.wordpress.com/704/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/nesteruk.wordpress.com/704/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/nesteruk.wordpress.com/704/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/nesteruk.wordpress.com/704/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/nesteruk.wordpress.com/704/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/nesteruk.wordpress.com/704/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/nesteruk.wordpress.com/704/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/nesteruk.wordpress.com/704/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/nesteruk.wordpress.com/704/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/nesteruk.wordpress.com/704/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=704&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://nesteruk.wordpress.com/2011/01/19/on-websharper-and-sitelets/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f01cbd2238e2a78e4c43fa596f51d6a1?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=G" medium="image">
			<media:title type="html">nesteruk</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/0/1ac970e1-40cf-4dc7-ac88-adc39a02a8e5.png" medium="image" />
	</item>
		<item>
		<title>Технологии на моей тарелке</title>
		<link>http://nesteruk.wordpress.com/2011/01/07/tech-on-my-plate/</link>
		<comments>http://nesteruk.wordpress.com/2011/01/07/tech-on-my-plate/#comments</comments>
		<pubDate>Fri, 07 Jan 2011 16:44:01 +0000</pubDate>
		<dc:creator>Dmitri</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[f#]]></category>
		<category><![CDATA[GPU]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[dotnetrdf]]></category>
		<category><![CDATA[good relations]]></category>
		<category><![CDATA[gpu.net]]></category>
		<category><![CDATA[mindtouch]]></category>
		<category><![CDATA[netkernel]]></category>

		<guid isPermaLink="false">http://nesteruk.wordpress.com/?p=694</guid>
		<description><![CDATA[За последние несколько месяцев, мне под руки попался ряд технологий которые я, без стороннего влияния, наверняка никогда бы и не узнал. Причина этому во-первых в том, что я слишком сильно &#8220;залип&#8221; в инфраструктуре .Net и языке C# чтобы отчетливо видеть что-то за ее пределами. Тем самым, &#8220;отлипание&#8221; для меня &#8211; процесс болезненный. А целью этого [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=694&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>За последние несколько месяцев, мне под руки попался ряд технологий которые я, без стороннего влияния, наверняка никогда бы и не узнал. Причина этому во-первых в том, что я слишком сильно &ldquo;залип&rdquo; в инфраструктуре .Net и языке C# чтобы отчетливо видеть что-то за ее пределами. Тем самым, &ldquo;отлипание&rdquo; для меня &ndash; процесс болезненный. А целью этого поста является кратко осветить те технологии, с которыми я познакомился &ndash; мало ли, может кому-то еще интересно будет.</p>
<h3>F#</h3>
<p>Писать на F# демо-программки и делать на нем что-то существенное &ndash; это совершенно разные вещи. Когда начинаешь массово плодить код на F#, начинаешь понимать всю упадочность своего понимания мира. Например, я достаточно долго писал <code>member this.Something with get() = ...</code> вместо аналогичного варианта <code>member this.Something = ...</code>. Ну и тяга к скобочкам и излишним аннотациям типов (даже там где и ежу понятно что это за тип будет) &ndash; это тоже наследие использования C# где все прописывается.</p>
<p>F# поражает скудностью поддержки в студии. На одной из моих машин вылетела поддержка IntelliSense (полностью!), на другой &ndash; F# проекты по F5 только компилируются но не запускается отладчик. Очевидно что-то тут не так. Я конечно предпринимаю <a href="http://activemesa.com/fsharper" title="http://acivemesa.com/fsharper">усилия</a> для того чтобы работать с F# было более гуманно, но пока что нужно делать основные вещи, такие как лексер и парсер. Адаптировать их из сорцов F#-а &mdash; дело не для слабонервных.</p>
<h3>WebSharper</h3>
<p><img class="alignright" src="http://nesteruk.org/pix/0/1ac970e1-40cf-4dc7-ac88-adc39a02a8e5.png" alt="" style="border:0;" align="right" />Я упомянул <a href="http://websharpercom" title="http://websharpercom">этот фреймворк</a> в моем <a href="http://spbaltnet.podfm.ru/solo/28/" title="http://spbaltnet.podfm.ru/solo/28/">первом сольном подкасте</a>, сказав что он позволяет делать сайты, полностью оторваные от Asp.Net и IIS. Не знаю, один ли я страдаю такой неприязнью к IIS, но на данный момент для меня нет антидота: тот релиз Вебшарпера который состоялся недавно еще не поддерживает вторую версию Sitelet&rsquo;ов, в которой как раз и должна быть поддержка создания полностью независимых сайтов. Что-то мне подсказывает, правда, что Адам Границ и компания планируют вообще сделать чуть ли не свой веб-сервер.</p>
<p>Так или иначе, я продолжаю эксперименты с Web#, а как только появятся новые сайтлеты, я начну тестировать все это дело уже на Linux. Интересно, что хоть технология и F#, нельзя сказать что она как-то связана с .Net или Microsoft.</p>
<h3>NetKernel</h3>
<p><img src="http://t2.gstatic.com/images?q=tbn:ANd9GcR4nk9TsFsbj11Nt2PHBYMH8qoedNLzedu7yvv6HuLx5gPR1k8Z" alt="" class="alignright" style="border:0;" align="right" />Во-первых, NK &ndash; это Java-стек, но меня это не пугает т.к. ничего страшного там нет. NK &ndash; это реализация ресурсно-ориентированной парадигмы, которая сейчас будоражит умы всех кто занимается коммуникационными технологиями, например та же комманда WCF. По сути дела, RoC в представлении NK &ndash; это REST&rsquo;ообразная концепция, которая предоставляет как бы аусторсинг веб-архитектуры, позволяя разработчикам писать компоненты, которые работают через ресурсы, запросы на ресурсы, конверсию различных типов ресурсов, и так далее.</p>
<p>NK это, если хотите, эволюция REST, и является интересной технологией (более живой чем существующие .Net аналоги) которая понравится тем, кто доселе работал с такими вещами как WCF REST.</p>
<h3>Семантический Веб</h3>
<p>Про семантический веб я <a href="http://nesteruk.wordpress.com/2010/12/12/a-few-words-on-semantic-web/" title="http://nesteruk.wordpress.com/2010/12/12/a-few-words-on-semantic-web/">уже писал</a>, поэтому ограничусь конкретикой, а именно теми библиотеками, которые применимы к данной задаче.</p>
<p>Во-первых, это <a href="http://www.dotnetrdf.org/" title="http://www.dotnetrdf.org/">DotNetRdf</a> &ndash; собственно библиотека, которая позволяет работать с RDF и поддерживает различные форматы хранилищ. Кстати, недавно нашел в блоге <a href="http://blog.abodit.com/2011/01/a-semantic-web-ontology-triple-store-built-on-mongodb/" title="http://blog.abodit.com/2011/01/a-semantic-web-ontology-triple-store-built-on-mongodb/">попытку создания</a> semantic triple store на MongoDB.</p>
<p>Еще один аспект моих исследований связан уже с конкретикой, а именно с онтологией <a href="http://www.heppnetz.de/projects/goodrelations/" title="http://www.heppnetz.de/projects/goodrelations/">Good Relations</a> и ее нынешней поддержкой гуглом (их Rich Snippets). Изначально у меня была идея попробовать подобавляеть поддержку всего этого в <a href="http://activemesa.com/r2p" title="http://activemesa.com/r2p">R2P</a> чтобы с этим было легче работать, но потом я узнал что модели Xml и Html в моем любимом Решарпере не имеют общего корня, что означает что, по сути дела, все пришлось бы делать дважды. Возможно когда-нибудь у меня дойдут до этого руки.</p>
<p>Так вот, в попытках найти возможность создать .Net объектную модель из OWL-файла, описывающего GR, я набрел на <a href="http://rowlex.nc3a.nato.int/" title="http://rowlex.nc3a.nato.int/">проект ROWLEX</a>, в котором поставляется утилита именно для этого. Интересно, а можно ли как-то автоматически брать данные со страниц, записывать их в эту модель и потом заталкивать все в MongoDB без каких-либо лишних действий? Подозреваю что все несколько сложнее&hellip;</p>
<h3>MindTouch</h3>
<p><img src="http://t2.gstatic.com/images?q=tbn:ANd9GcSRdCfnPy473CyduJ48wFbv0fv5qDkZ8S2E-wY_AgMyU9kAWG3D" alt="" style="border:0;" class="alignright" align="right" />Я занимался исследованием <a href="http://mindtouch.com" title="http://mindtouch.com">MindTouch</a> в двух направлениях &ndash; в попытке понять, можно ли использовать эту штуку для документирования API большого программного продукта, а также мне хотелось понять, можно ли использовать его вместо SharePoint для внутренних целей.</p>
<p>По первому вопросу у меня нет ответа. Бесплатная версия MT шикарна, и покрывает 99% того что мне лично нужно. Поэтому в личных целях я начал ее использовать. Что же касается построения на нем портала разработчиков, то тут все неоднозначно. Я не decision maker в данном вопросе, но лично меня никто не убедил отдавать за коммерческий вариант этой штуки большие деньги.</p>
<p>Я рекоммендую всем скачать MindTouch Core (бесплатную версию) и поиграть с ней. Это действительно стоит того! Это по сути дела Wiki/CMS которая хорошо подходит для документации. Чудес в этом мире не бывает, но штука красивая, и я ей доволен.</p>
<h3>TidePowerd GPU.NET</h3>
<p>Поскольку несколько лет назад я плотно занимался GPGPU и даже сам эксплуатировал pixel shader&rsquo;ы через Direct3D, наличие .Net библиотеки мне импонирует &ndash; давно пора. Тем более что трансляция IL во что-то еще &ndash; процесс весьма понятный и реализуемый без особых проблем. Библиотека <a href="http://www.tidepowerd.com/" title="http://www.tidepowerd.com/">GPU.Net</a> &ndash; это как раз оно и есть. Еще одна абстракция чтобы облегчить нам жизнь.</p>
<p><del>Признаюсь что я только лишь посмотрел вебинар &ndash; я не игрался еще с самой библиотекой т.к. не было времени. Все впереди :)</del><strong>Update: </strong>уже успел созвониться с директором Tidepowerd &ndash; разговор шел в основном в ключе поддержки конкретно F# в GPU.NET. Заинтересованность определенно есть, так что рано или поздно замутим совместный конференс с Саймом, Границем, и партнерами. Как что состоится &ndash; обязательно напишу.</p>
<h3>Fusion IO</h3>
<p><a href="http://www.fusionio.com/" title="http://www.fusionio.com/">Fusion IO</a> &ndash; компания, которая за 3 года получила 300 миллионов долларов инвестиций. Их &ldquo;фишка&rdquo; &ndash; высокоскоростные хранилища данных, и когда я говорю &ldquo;хранилище&rdquo;, подразумевается PCI-карта с SSD-устройствами на ней. Конечно, Fusion IO говорит что их устройства &ndash; это <em><font face="Candara" />не</font></em> SSD, но на самом деле их отличие в качественном ПО (в плане прошивки их ASIC), а есть ли у них что-то кардинально новое &ndash; не знаю. Надо получить образец (надеюсь дадут) и самому посмотреть.</p>
<p>Хотя если по мне, так более интересной фишкой является <a href="http://ru.wikipedia.org/wiki/Nvidia_Tegra" title="http://ru.wikipedia.org/wiki/Nvidia_Tegra">NVidia Tegra</a> &ndash; нам уже 10 лет назад нужно было иметь нормальные <a href="http://en.wikipedia.org/wiki/System-on-a-chip" title="http://en.wikipedia.org/wiki/System-on-a-chip">SoC</a>-решения. Те же Fusion IO могли бы засунуть на свои карты контроллер и несколько FPGA которые я бы мог &ldquo;флэшить&rdquo; и им цены бы не было! Вот интересно &ndash; Intel уже начал нервничать или еще нет? Ведь по мне так самое главное это избавиться от ситуации когда я не могу иметь масштабно-инвариантную систему, а должен на каждый процессор закупать еще и материнскую плату, память, жесткий диск, и так далее. Надеюсь скоро эти телодвижения станут пережитками прошлого.</p>
<p>Кстати о Tegra&hellip; двухъядерные процессоры в мобильных телефонах это круто или как?</p>
<br />Filed under: <a href='http://nesteruk.wordpress.com/category/net/'>.NET</a>, <a href='http://nesteruk.wordpress.com/category/net/f/'>f#</a>, <a href='http://nesteruk.wordpress.com/category/gpu/'>GPU</a>, <a href='http://nesteruk.wordpress.com/category/hardware/'>Hardware</a> Tagged: <a href='http://nesteruk.wordpress.com/tag/dotnetrdf/'>dotnetrdf</a>, <a href='http://nesteruk.wordpress.com/tag/good-relations/'>good relations</a>, <a href='http://nesteruk.wordpress.com/tag/gpu-net/'>gpu.net</a>, <a href='http://nesteruk.wordpress.com/tag/mindtouch/'>mindtouch</a>, <a href='http://nesteruk.wordpress.com/tag/netkernel/'>netkernel</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/nesteruk.wordpress.com/694/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/nesteruk.wordpress.com/694/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/nesteruk.wordpress.com/694/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/nesteruk.wordpress.com/694/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/nesteruk.wordpress.com/694/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/nesteruk.wordpress.com/694/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/nesteruk.wordpress.com/694/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/nesteruk.wordpress.com/694/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/nesteruk.wordpress.com/694/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/nesteruk.wordpress.com/694/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/nesteruk.wordpress.com/694/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/nesteruk.wordpress.com/694/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/nesteruk.wordpress.com/694/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/nesteruk.wordpress.com/694/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=694&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://nesteruk.wordpress.com/2011/01/07/tech-on-my-plate/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f01cbd2238e2a78e4c43fa596f51d6a1?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=G" medium="image">
			<media:title type="html">nesteruk</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/0/1ac970e1-40cf-4dc7-ac88-adc39a02a8e5.png" medium="image" />

		<media:content url="http://t2.gstatic.com/images?q=tbn:ANd9GcR4nk9TsFsbj11Nt2PHBYMH8qoedNLzedu7yvv6HuLx5gPR1k8Z" medium="image" />

		<media:content url="http://t2.gstatic.com/images?q=tbn:ANd9GcSRdCfnPy473CyduJ48wFbv0fv5qDkZ8S2E-wY_AgMyU9kAWG3D" medium="image" />
	</item>
		<item>
		<title>Ревью нескольких .Net-ориентированных книг от Manning</title>
		<link>http://nesteruk.wordpress.com/2010/08/27/manning-dotnet-book-review/</link>
		<comments>http://nesteruk.wordpress.com/2010/08/27/manning-dotnet-book-review/#comments</comments>
		<pubDate>Fri, 27 Aug 2010 11:07:56 +0000</pubDate>
		<dc:creator>Dmitri</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[Books]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[f#]]></category>

		<guid isPermaLink="false">http://nesteruk.wordpress.com/?p=563</guid>
		<description><![CDATA[<p><img src="http://nesteruk.org/pix/0/de713ad9-ee79-44d6-83dc-af770df02bca.jpg" class="alignleft" alt="" style="border:0;margin-right:10px;" />Это еще один пост с обзором книг, но в этот раз «с изюминкой» &#8211; будем обсуждать книги издательства <a href="http://www.manning.com/" title="http://www.manning.com/">Manning</a>. По сравнению с Apress (см. <a href="http://nesteruk.wordpress.com/2010/08/22/apress-dotnet-book-review/" title="http://nesteruk.wordpress.com/2010/08/22/apress-dotnet-book-review/">предыдущий обзор</a>), издательство Manning Publications печатают книги совсем другого уровня. У них, например, печатаются такие люди как Джон Скит или Айенде. Книги от Manning &#8211; это далеко не утилитарные произведения, что с одной стороны хорошо (технический уровень намного выше) а с другой стороны не очень (книги иногда сложно читать, и многие из них недоступны для новичков).<br /><br />Заметьте, тут всего 4 книжки. Хочется больше, но как-то руки не доходят попросить у Manning несколько review-копий.</p><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=563&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><img src="http://nesteruk.org/pix/0/de713ad9-ee79-44d6-83dc-af770df02bca.jpg" class="alignleft" alt="" style="border:0;margin-right:10px;" />Это еще один пост с обзором книг, но в этот раз «с изюминкой» &ndash; будем обсуждать книги издательства <a href="http://www.manning.com/" title="http://www.manning.com/">Manning</a>. По сравнению с Apress (см. <a href="http://nesteruk.wordpress.com/2010/08/22/apress-dotnet-book-review/" title="http://nesteruk.wordpress.com/2010/08/22/apress-dotnet-book-review/">предыдущий обзор</a>), издательство Manning Publications печатают книги совсем другого уровня. У них, например, печатаются такие люди как Джон Скит или Айенде. Книги от Manning &ndash; это далеко не утилитарные произведения, что с одной стороны хорошо (технический уровень намного выше) а с другой стороны не очень (книги иногда сложно читать, и многие из них недоступны для новичков).</p>
<p>Заметьте, тут всего 4 книжки. Хочется больше, но как-то руки не доходят попросить у Manning несколько review-копий.</p>
<hr />
<h3 style="margin-top:15px;">DSLs in BOO</h3>
<p>
<img src="http://nesteruk.org/pix/0/ed1384b6-bd51-46d8-86e2-ca04ed6e134a.jpg" class="alignright" align="right" alt="" /><br />
Собственно сам язык Boo (некоторые почему-то пишут BOO) мне интересен только одной фичей &ndash; поддержкой метапрограммирования или, другими словами, возможностью расширять компилятор. Привлекательно это потому, что научиться конкретно метапрограммированию в Boo не так сложно, хотя и приходится отказаться от таких приятностей как синтаксис C#, Visual Studio (нужно использовать SharpDevelop) и соответственно IntelliSense. Как бы там ни было, популярный блоггер <a href="http://www.ayende.com/" title="http://www.ayende.com/">Айенде</a> решил встать «впереди планеты всей» и написал книжку про то, как создавать доменно-специфичные языки (DSL-и) на этом питонообразном языке.
</p>
<p>Если быть откровенным, то книжка у Айенде <em><font face="Trebuchet MS" />не получилась</font></em>. Совсем. Главной причиной является то, что Айенде плохо пишет &ndash; у него явно нет навыков для написания больших монографий, и с первых страниц книги читатель может ощутить тот дискомфорт который автор испытывает, пытаясь передать мысль в когерентной форме. Теперь насчет собственно структуры контента. Во-первых, Айенде плохо продает идею DSLей &ndash; а ее, как мне кажется, <em><font face="Candara" />нужно</font></em> продавать. Например, показать пример BDD-фреймворка и сказать что это DSL &ndash; это как-то нечестно потому что я, например, могу привести контр-пример где мы используем, скажем, Cucumber (Ruby) чтобы написать то же самое, и для этого мне не нужен Boo. Возникает естественный вопрос: а что нам дает именно Boo, чем он лучше и полезнее?</p>
<p>Далее в книге идет описание самого языка, и интересно что на создание DSLей на этом языке выделено всего 2 главы из 13 &ndash; остальное это либо вводные части, либо примеры реализаций. Кстати насчет примеров &ndash; они все не просто короткие, они <em><font face="Candara" />коротенькие</font></em> и ультра-простые. Это я к тому, что в моей практике DSLи имеет смысл использовать тогда, когда за одной строчкой DSL скрывается 5-10 обычного C#. У Айенде же в примерах соотношение примерно 1:1. Зато количество инфраструктурных обсуждений в книге очень большое &ndash; тестирование, создание UI, да просто обсуждение того как сортировать скрипты &ndash; возникает вопрос о смещении ценностей. Ведь все-таки изначальная идея была продать Boo как язык для создания DSLей, а в результате она как бы вышла на второй план.</p>
<p>В заключение скажу что на книгу я возлагал много надежд, купив ее в форме early access для подготовки к небольшому <a href="http://www.slideshare.net/DmitriNesteruk/metaprogramming-2349505" title="http://www.slideshare.net/DmitriNesteruk/metaprogramming-2349505">семинару по метапрограммированию</a>, но в результате разочаровался. Что ж, иногда и так бывает.</p>
<p><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="1 stars" /><img src="http://nesteruk.org/pix/star-none.gif" style="border:0;margin:0;padding:0;" alt="1 stars" /><img src="http://nesteruk.org/pix/star-none.gif" style="border:0;margin:0;padding:0;" alt="1 stars" /><img src="http://nesteruk.org/pix/star-none.gif" style="border:0;margin:0;padding:0;" alt="1 stars" /><img src="http://nesteruk.org/pix/star-none.gif" style="border:0;margin:0;padding:0;" alt="1 stars" /></p>
<h3>ASP.NET MVC 2 in Action</h3>
<p><img src="http://nesteruk.org/pix/0/9218131b-e12b-4044-a725-f4e63a213ff5.jpg" class="alignright" align="right" alt="" /><br />
Иначе как «вынос мозга» эту книжку не обозвать. MVC in Action &ndash; это книга по Asp.Net MVC для тех кто уже похоже все знает но хочет проверить свои знания и заодно получить дозу зубодробильной информации от настоящих гуру в этом деле. Написало ее целых 5 авторов (все MVP), и они за словом в карман не лезут &ndash; тут у них и <a href="http://mvccontrib.codeplex.com/" title="http://mvccontrib.codeplex.com/">MvcContrib</a> и <a href="http://www.ayende.com/projects/rhino-mocks.aspx" title="http://www.ayende.com/projects/rhino-mocks.aspx">Rhino.Mocks</a> и другие прелести.
</p>
<p>На самом деле &ndash; первая часть книги это еще цветочки &ndash; ее хоть как-то можно читать новичкам. А вот начиная со 2й части уже становится значительно сложнее, т.к. без хорошего багажа знаний современный паттернов и практик во всем написанном вполне реально утонуть. Тут, кстати, фигурирут такие вещи как <a href="http://en.wikipedia.org/wiki/Domain-driven_design" title="http://en.wikipedia.org/wiki/Domain-driven_design">DDD</a> и такие технологии как <a href="http://sparkviewengine.com/" title="http://sparkviewengine.com/">Spark</a>. (Это кстате в каком-то смысле минус т.к. большинство людей сейчас скорее интересует Razor.) В трерьей части книги появляются такие вещи как AutoMapper и NHibernate (опять же, все преподнесено на хорошем, <em><font face="Candara" />презентабельном</font></em> уровне). Ну и в последней главе, уже в разбивку, показаны разные многофункциональные прелести. В одной из глав, правда, приведен пример того, как написать autocomplete text-box, что немножко выбивает из ритма.</p>
<p>Если резюмировать &ndash; хорошая, компактная но полная актуальной информации «от станка» книжка. Подойдет для тех кто уже начал писать на MVC и теперь хочет сделать что-то серьезное. Рекоммендую!</p>
<p><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="4 stars" /><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="4 stars" /><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="4 stars" /><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="4 stars" /><img src="http://nesteruk.org/pix/star-half.gif" style="border:0;margin:0;padding:0;" alt="4 stars" /></p>
<h3>C# in Depth</h3>
<p><img src="http://nesteruk.org/pix/0/fae964fd-7ba0-4af9-8d13-e9dd180637a4.jpg" class="alignright" align="right" alt="" />Несмотря на то, что у меня есть свои фавориты в плане книжек по C#, я счел своим долгом прочитать именно эту &ndash; ведь её автором является не кто-то там, а сам Джон «Чак Норрис» Скит. Для тех из вас кто живет на другой планете, мистер Скит является <a href="http://stackoverflow.com/users/22656/jon-skeet" title="http://stackoverflow.com/users/22656/jon-skeet">звездой портала StackOverflow</a> и одним из ведущих экспертов по C# (а также по Java, на которой он пишет в Google, хотя подозреваю что его интерес к этому языку несколько меньше по весьма понятным причинам).</p>
<p>Итак, если коротко &ndash; то C# in Depth (я читал первую версию, сейчас <a href="http://www.manning.com/skeet2/" title="http://www.manning.com/skeet2/">есть вторая</a>) &ndash; это <em><font face="Candara" />просто конфетка</font></em>. Для человека который хочет узнать что такое C# (например, если вы пришли к нему из мира Java или C++), эта книга &ndash; манна небесная. Написана она очень хорошо &ndash; в очередной раз подтверждает что мы британцы в принципе неплохо пишем :)</p>
<p>Книга, как вы догадались, является хрестоматией C# разработки, начиная от азов и кончая такими вещами как Linq. Читается она очень легко, без WTF-моментов. Скит пишет в очень неформальной манере, завлекая читателя в интересные тонкости языка. Я читал много книг по C#, но такой дозы позитива я до этого не находил нигде. Мне кажется, что эта книга формирует неплохой «двойной удар» с книжкой C# in a Nutshell (о книгах O&rsquo;Reilly мы еще поговорим) &ndash; ведь первая объясняет что это за язык такой, а вторая описывает библиотеки классов.</p>
<p>Вообщем, впечатление осталось очень позитивное, поэтому C# in Depth получает твердую пятерку как за стиль так и за содержание.</p>
<p><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="5 stars" /><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="5 stars" /><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="5 stars" /><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="5 stars" /><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="5 stars" /></p>
<h3>Real-World Functional Programming</h3>
<p><img src="http://nesteruk.org/pix/0/a1d34557-7c01-4c22-99f5-76fefd59d2d5.jpg" class="alignright" align="right" alt="" />После того как я опубликовал отзывы по книге Expert F#, коллеги подсказали что неплохо было бы мне было посмотреть и на эту книгу, благо не всем хочется пролистывать дорогой (одна из самых дорогих книг) талмуд на тему F#. Также, стимулировали к прочтению этой книги две вещи &ndash; то что книжка покрывает не только F# но и C#, а также (drumroll please) то что и к этой книге приложил руку сам Джон Скит (хотя основной автор &ndash; Томаш Петричек). Сорри если кого-то этот мем начал раздражать &ndash; я не нарочно.</p>
<p>Итак, R-WFP &ndash; это книжка про «бытовое» функциональное программирование без высоко поднятого носа сообществ языка Haskell, Lisp или каких-то еще &ndash; это, в очередной раз, <em><font face="Candara" />прагматика</font></em> и лично мне это очень нравится. (Собственно термин &lsquo;Real-World&rsquo; в названии как бы намекает&hellip;) Первое что пришло мне в голову &ndash; они украли у меня идею функциональной анимации! Я недавно на эту тему <a href="http://habrahabr.ru/blogs/net/57236/" title="http://habrahabr.ru/blogs/net/57236/">статью писал</a>, она даже <a href="http://www.codeproject.com/KB/dotnet/lambdawpf.aspx" title="http://www.codeproject.com/KB/dotnet/lambdawpf.aspx">на CP приз выиграла</a>. Впрочем, очень приятно что авторы пишут про знакомые мне идеи. Также приятно что не остались за бортом и другие языки программирования &ndash; такие как Lisp и Erlang. Понятное дело что мы тут про .Net говорим, но наше сообщество порой страдает узким мировозрением по отношению к другим платформам.</p>
<p>Книга, точно так же как и C# in Depth, делает плавную прогрессию от азов (например, от рассказов про то что такое кортежи или списки &ndash; это ведь нужно для понимания F#) и прогрессируя до таких вещей как применение функций высокого порядка. Вообще что меня поразило, так это широта задач, которые покрывает эта книга &ndash; тут и анимация, и создание DSLей (я правда и сам писал про DSLи на F#) и реактивное программирование. Правда тут есть один весьма существенный (с моей точки зрения) минус &ndash; поскольку книга была опубликована в декабре 2009, в книге не слова на тему Reactive Extensions. <em><font face="Candara" />Обидно!</font></em> Ведь не знаю как вам, а лично мне очень хотелось бы иметь описание тех новых структур и опереторов, которые там фигурируют, а также четкое руководство к использованию. Хоть я и сам, в принципе, понимаю что и как.</p>
<p>Скажу честно, книга мне понравилась &ndash; в ней нашлось предостаточно моментов (особенно по F#, где мои знания на «четыре с минусом») которые меня удивили и заставили пересмотреть то как я пишу код. Другое дело что у меня очень мало F# «в продашкн», но это уже чисто мои проблемы.</p>
<p><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="4 stars" /><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="4 stars" /><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="4 stars" /><img src="http://nesteruk.org/pix/star.gif" style="border:0;margin:0;padding:0;" alt="4 stars" /><img src="http://nesteruk.org/pix/star-none.gif" style="border:0;margin:0;padding:0;" alt="4 stars" /></p>
<br />Filed under: <a href='http://nesteruk.wordpress.com/category/net/'>.NET</a>, <a href='http://nesteruk.wordpress.com/category/books/'>Books</a>, <a href='http://nesteruk.wordpress.com/category/c/'>C#</a>, <a href='http://nesteruk.wordpress.com/category/net/f/'>f#</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/nesteruk.wordpress.com/563/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/nesteruk.wordpress.com/563/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/nesteruk.wordpress.com/563/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/nesteruk.wordpress.com/563/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/nesteruk.wordpress.com/563/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/nesteruk.wordpress.com/563/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/nesteruk.wordpress.com/563/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/nesteruk.wordpress.com/563/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/nesteruk.wordpress.com/563/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/nesteruk.wordpress.com/563/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/nesteruk.wordpress.com/563/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/nesteruk.wordpress.com/563/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/nesteruk.wordpress.com/563/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/nesteruk.wordpress.com/563/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=563&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://nesteruk.wordpress.com/2010/08/27/manning-dotnet-book-review/feed/</wfw:commentRss>
		<slash:comments>31</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f01cbd2238e2a78e4c43fa596f51d6a1?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=G" medium="image">
			<media:title type="html">nesteruk</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/0/de713ad9-ee79-44d6-83dc-af770df02bca.jpg" medium="image" />

		<media:content url="http://nesteruk.org/pix/0/ed1384b6-bd51-46d8-86e2-ca04ed6e134a.jpg" medium="image" />

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">1 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star-none.gif" medium="image">
			<media:title type="html">1 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star-none.gif" medium="image">
			<media:title type="html">1 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star-none.gif" medium="image">
			<media:title type="html">1 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star-none.gif" medium="image">
			<media:title type="html">1 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/0/9218131b-e12b-4044-a725-f4e63a213ff5.jpg" medium="image" />

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">4 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">4 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">4 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">4 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star-half.gif" medium="image">
			<media:title type="html">4 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/0/fae964fd-7ba0-4af9-8d13-e9dd180637a4.jpg" medium="image" />

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">5 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">5 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">5 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">5 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">5 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/0/a1d34557-7c01-4c22-99f5-76fefd59d2d5.jpg" medium="image" />

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">4 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">4 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">4 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star.gif" medium="image">
			<media:title type="html">4 stars</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/pix/star-none.gif" medium="image">
			<media:title type="html">4 stars</media:title>
		</media:content>
	</item>
		<item>
		<title>Нужно ли полиглотное программирование в стеке .Net?</title>
		<link>http://nesteruk.wordpress.com/2010/08/02/dot-net-polyglot-programming/</link>
		<comments>http://nesteruk.wordpress.com/2010/08/02/dot-net-polyglot-programming/#comments</comments>
		<pubDate>Mon, 02 Aug 2010 13:55:44 +0000</pubDate>
		<dc:creator>Dmitri</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[f#]]></category>

		<guid isPermaLink="false">http://nesteruk.wordpress.com/?p=490</guid>
		<description><![CDATA[Из комментариев к коротенькому посту на Хабре про использование Python как базового языка для Asp.Net мне понравился вот этот комментарий от centur: Ну собственно повторный вопрос &#8212; зачем? Дмитрий Нестерук высказал вполне здравую мысль &#8212; зачем нужна вся эта куча языков, если есть так активно развиваемый C#. В общем возможность мульти-языкастости среды это клево, но [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=490&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Из комментариев к <a href="http://habrahabr.ru/blogs/python/100158/">коротенькому посту</a> на Хабре про использование Python как базового языка для Asp.Net мне понравился вот этот комментарий от <a href="http://centur.habrahabr.ru">centur</a>:</p>
<blockquote><p>
Ну собственно повторный вопрос &mdash; зачем?<br />
Дмитрий Нестерук высказал вполне здравую мысль &mdash; зачем нужна вся эта куча языков, если есть так активно развиваемый C#.<br />
В общем возможность мульти-языкастости среды это клево, но вот потенциальная поддержка этого &mdash; один из кругов ада =)
</p></blockquote>
<p>Мне кажется что пришло самое время досконально обсудить проблему того, что конкретно нам дают все эти языки, и насколько &ldquo;полиглотное программирование&rdquo; вообще имеет смысл.</p>
<h3>F#</h3>
<p>Давайте прокинем VB и начнем с языка №2 который уже является полноправным жителем нашей .Net экосистемы. Да, интересный, функциональный язык. А теперь внимание вопрос: в чем собственно его приемущества, какой конкретно от него business value?</p>
<p>Вот я написал несколько проектов либо полностью на F# либо частично. У меня была возможность попробовать такие &ldquo;рекламируемые&rdquo; фичи как async workflows и повсеместное отсутствие мутабельности. И вот к каким выводам я пришел:</p>
<ul>
<li>Бизнес-программирование по большому счету мутабельное, тем самым язык который вставляет вам 100 палок в каждое колесо при попытке изменить переменную для него слабо подходит. Чего стоит например вот такое определение:<br />
<pre class="brush: fsharp;">
let context : Ref&lt;IVisitable option&gt; = ref None
</pre>  </li>
<li>Async workflows &ndash; это всего лишь кастомная издевка над синтаксисом. Это хак. Такой же хак есть и в .Net с использованием AsyncEnumerator (PowerThreading). Ничего &ldquo;гениального&rdquo; в этом нет, работу это не упрощает. Вот вам прямое сравнение двух подходов &ndash; выводы делайте сами:
<p>
  <strong>F#</strong>
</p>
<p><pre class="brush: fsharp;">
let private DownloadPage(url:string) =
  async {
    try
      let r = WebRequest.Create(url)
      let! resp = r.GetResponseAsync() // let! позволяет дождаться результата
      use stream = resp.GetResponseStream()
      use reader = new StreamReader(stream)
      let html = reader.ReadToEnd()
      use fs = new FileStream(@&quot;c:\temp\file.htm&quot;, FileMode.Create,
                              FileAccess.Write, FileShare.None, 1024, true);
      let bytes = Encoding.UTF8.GetBytes(html);
      do! fs.AsyncWrite(bytes, 0, bytes.Length) // ждем пока все запишется
    with
      | :? WebException -&gt; ()
  }
// вызов ниже делает синхронный вызов метода, но поведение внутри метода - асинхронное
Async.RunSynchronously(DownloadPage(&quot;http://habrahabr.ru&quot;))
</pre>
<p>
  <strong>C#</strong>
</p>
<p><pre class="brush: csharp;">
public IEnumerator&lt;int&gt; DownloadPage(string url, AsyncEnumerator ae)
{
  var wr = WebRequest.Create(url);
  wr.BeginGetResponse(ae.End(), null);
  yield return 1;
  var resp = wr.EndGetResponse(ae.DequeueAsyncResult());
  var stream = resp.GetResponseStream();
  var reader = new StreamReader(stream);
  string html = reader.ReadToEnd();
  using (var fs = new FileStream(@&quot;c:\temp\file.htm&quot;, FileMode.Create,
                      FileAccess.Write, FileShare.None, 1024, true))
  {
    var bytes = Encoding.UTF8.GetBytes(html);
    fs.BeginWrite(bytes, 0, bytes.Length, ae.End(), null);
    yield return 1;
    fs.EndWrite(ae.DequeueAsyncResult());
  }
}
</pre>  </li>
<li>Метапрограммирование в F# <em>неполноценно</em>. То, что там есть цитирование, еще ничего не значит, т.к. нам хочется не транслировать F# в какой-то там язык, мы хотим то цитирование и &ldquo;сплайс&rdquo; которыми мы можем помочь компилятору сгенерировать некие структуры в процессе компиляции. Настоящее статическое метапрограммирование есть пока что только в Boo, но оно <em>будет</em> в C#.</li>
<li>Создание DSLей &ndash; да, оно проще. Если интересно &ndash; <a href="http://www.codeproject.com/KB/dotnet/dslfsharp.aspx">вот моя статья</a>. Только DSLи &ndash; это пока направление которое в основном, во-первых, для internal use, а во-вторых только для продвинутых фирм вроде моей. Точно не для общего пользования. Не верите? <a href="http://lmgtfy.com/?q=f%23+dsl">Let me google that for you</a>.</li>
<li>Идея о том, что F# лучше чем C# для сложного алгоритмического программирования в финансовых и аналогичных индустриях <em>не раскрыта</em>. Да, его используют. Детальных case studies мало, и опять же, никто толком не может объяснить <em>почему</em>. И вообще, какая разница, во что <a href="http://www.codeproject.com/KB/recipes/mmlsharp.aspx">транслировать формулы</a>?</li>
</ul>
<p>Есть еще нарекания в плане &ldquo;общей вменяемости&rdquo; &ndash; например набор всяких <code>&lt;@ @&gt;</code>, <code>&lt;@@ @@&gt;</code>, <code>[]</code>, <code>[| |]</code>, <code>{}</code>, <code>(|x|_|)</code>, <code>-&gt;</code>, <code>&lt;-</code>, <code>&gt;&gt;</code>, <code>:&gt;</code>, <code>:?&gt;</code>, <code>|&gt;</code> это же просто бред какой-то! Это &ldquo;магические операторы&rdquo;, иначе не назвать.</p>
<p>Вообщем, я действительно <em>хочу</em> использовать F#, но когда я реально начинаю его использовать, то каждая из фич которая изначально кажется selling point языка оказывается неповоротливой и непрактичной. Например, я надеялся что pattern matching поможет мне структурировать разбор строк вот <a href="http://bitbucket.org/nesteruk/csharpshorthand">в этом проекте</a>. На самом же деле, код получился <a href="http://nesteruk.wordpress.com/2010/03/05/c-zen-coding-%D1%83%D0%B6%D0%B5-%D0%BD%D0%B0-f/">практически нечитабельным</a>. Где тут maintainability? Правильно &ndash; ее нет в помине.</p>
<h3>Boo</h3>
<p>Я конечно понимаю что товарищ <a href="http://ayende.com/">Айенде</a>, будучи &ldquo;впереди планеты всей&rdquo; в плане работы с базами данных, решил поделиться своими замечаниями на тему того, как метапрограммирование на языке Boo может принести реальный business value. Проблема в том, что книжка у него отровенно не удалась, и тем самым аргумент как бы затух.</p>
<p>Идея Boo в том, что у вас есть якобы более лаконичный синтаксис (а-ля Python) плюс возможности расширения компилятора которые делают возможным мета-изыски которых мы затаив дыхание ждем в C#. Ну так что, давайте обсудим эти два &ldquo;бенефита&rdquo; на предмет их полезности.</p>
<p>Вообще, идея того что Питон чем-то &ldquo;лаконичней&rdquo; чем C# или какой-то там другой язык &ndash; это попытка продать абстрактное &ldquo;нечто&rdquo;, не подкрепляя это толком никакими аргументами. Возьмем например произвольный кусок кода из проекта:</p>
<p><pre class="brush: python;">
class ManaIndicatorsAttribute(AbstractAstAttribute):   
  public override def Apply(node as Node):
    c = node as ClassDefinition
    for i in range(ManaSumAttribute.LandTypes.Count):
      basic = ManaSumAttribute.LandTypes[i] as string
      hybridLands as List = []
      for j in range(HybridManaAttribute.HybridLandTypes.Count):
        hybrid = HybridManaAttribute.HybridLandTypes[j] as string
        if (hybrid.Contains(basic)):
          hybridLands.Add(hybrid)
      rbasic = ReferenceExpression(basic.ToLower())
      b = Block();
      b1 = [| return true if $rbasic &gt; 0 |]
      b.Statements.Add(b1)
      for k in range(hybridLands.Count):
        rhybrid = ReferenceExpression((hybridLands[k] as string).ToLower())
        b2 = [| return true if $rhybrid &gt; 0 |]
        b.Statements.Add(b2)
      r = [|
        $(&quot;Is&quot; + basic):
          get:
            $b;
      |]
      c.Members.Add(r)
</pre>
<p>Ну-с, и где тут &lsquo;лаконичность&rsquo;? На практике получается жесткий, слабочитаемый код, в котором не спасает вывод типов, в котором записи вроде <code>return x if y</code> добавляют только дополнительную степень неоднозначиности, а вы бы видели IL который сгенерирован &ndash; у меня при виде того как код на Boo разворачивается в код на C# с помощью Рефлектора глаза на лоб лезут &ndash; такое впечатление что этому языку просто плевать на стэк. Поэтому простое суммирование пяти переменных переведенное в C# вернется вам не как <code>a + b + c + d + e</code> а скорее как <code>((((a + b) + c) + d) + e)</code>.</p>
<p>Насчет вещей, аналогичных LINQ. Да, действительно, до Linq в Питоне был козырь в плане разбора списков и прочих, т.е. можно было написать <code>list[1,-1]</code> или например подобное:
</p>
<p><pre class="brush: python;">
List(y for y in (x**2 for x in range(10)) if y % 3 != 0)
</pre>
<p>Теперь это <em>неактуально</em> т.к. в том же C# можно сделать то же самое, причем с поддержкой IntelliSense и без необходимости запоминать всякие <code>range()</code> (какая разница с <code>Enumerable.Range()</code>? меньше букв?) и прочие названия. Приемущество Linq в том, что это <em>унифицированный</em> API, который выглядит одинаково вне зависимости от того, используете ли вы Linq to Objects, Linq to Events (Rx) или Linq to CepStream (StreamInsight). Тут в основном одни и те же ключевые слова, плюс IntelliSense если вы что-то забыли.</p>
<p>Генераторы&hellip; ах, генераторы, идея-то хорошая, но вот скажите мне, что делает следующий кусочек кода:</p>
<p><pre class="brush: python;">
def TestGenerator():
  i = 1
  yield i
  for x in range(10):
    i *= 2
    yield i
</pre>
<p>Утипизация? Тем кому она нужна уже имеют ее в C# с помощью таких библиотек как <a href="http://github.com/philiplaureano/LinFu">LinFu</a>. Регулярные выражения через <code>/</code>? Ну что ж, может это и удобней, но лично мне все равно, т.к. я использую <a href="http://www.ultrapico.com/Expresso.htm">приложение</a>, которое решает за меня все эти проблемы (хотя соглашусь, что конверсия в C#-строку выглядит коряво). Что остается?</p>
<p>Остается пожалуй только метапрограммирование. Это, на данный момент, <em>единственный козырь</em> в языке который, как мы с коллегами считаем, умрет сразу же после выхода C#5. И если вы считаете что метапрограммивание нужно сегодня и нужно срочно использовать Boo, вот вам несколько контр-аргументов:</p>
<ul>
<li>Язык и инструментарий не развиваются. <a href="http://boolangstudio.codeplex.com/">BooLangStudio</a> мертва. Документация на сайтах частично устарела.</li>
<li>Использовать две IDE достаточно напряжно. Я имею ввиду, что вам придется компилировать Boo через <a href="http://www.icsharpcode.net/opensource/sd/">SharpDevelop</a>, а потом импортировать сборки в ваши C# проекты. Никакого configuration management рядом не стояло.</li>
<li>Компилятор сырой, и у меня были мета-методы которые вообще не сходились, т.е. программа компилировалась в невалидный IL. Никаких гарантий безопасности компилятор не дает.</li>
<li>Про IntelliSense или аналогичные вещи можно забыть &ndash; Boo вы будете программировать вслепую.</li>
</ul>
<p>Вообщем, для тех кого это действительно интересует, у меня есть вводная статья про метапрограммирование на Boo. Только вот стоит ли напрягаться? Ведь C#5 не за горами, и есть большая вероятность, что с его выходом можно будет пометить Boo как <code>[Obsolete]</code>.</p>
<h3>IronPython</h3>
<p>Вся критика по языку Boo может быть обращена к <a href="http://ironpython.net/">IronPython</a> ибо синтаксис похожий. Единственный плюс &ndash; что IronPython как бы более &ldquo;зрелый&rdquo; и даже имеет поддержку в Visual Studio (<a href="http://ironpython.net/tools/">IronPythonTools</a>). Ну и да, &ldquo;динамичность&rdquo; этого языка несет в себе, якобы, некоторые приемущества которые всем уже известны.</p>
<p>Только есть одно <em>но</em> &ndash; тема CLR vs DLR (не знаю можно ли так писать) &ndash; явно не раскрыта. Например, почему создатели языка Go намеренно сделали его статическим? Правильно &ndash; скорость. А что делать с языком который не дает ощутимых бенефитов в плане синтаксиса, и который по скорости не тянет (чего стоит скорость декораторов, например &ndash; да легче помучаться и на C# написать, или подвязать тот же <a href="http://www.sharpcrafters.com/">PostSharp</a>).</p>
<h3>IronRuby</h3>
<p>Если питонообразные языки человек с опытом C# еще как-то может понять, то Ruby &ndash; это чистая магия, т.е. язык который нужно долго и прагматично изучать с нуля. А стоит ли это того? Alt.Net сообщество уже прожужжало нам все уши на тему того, что Ruby лучше для тестирования (см. например <a href="http://cukes.info/">Cucumber</a>), и я с этим не спорю &ndash; действительно, чем ближе мы в BDD приближаемся к обычному английскому, тем больше к нам аппелирует сама концепция BDD. Только вот есть одно <em>но</em> &ndash; BDD пока штука не доказанная, она как и приемочное тестирование в стиле <a href="http://fitnesse.org/">Fitnesse</a> является интересной абстракцией к которой индустрия присматривается, но не делает ее стандартом для разработки.</p>
<p>Чем еще хорош Ruby? Да, он неплохо поддерживает <em>динамическое</em> метапрограммирование, есть даже <a href="http://pragprog.com/titles/ppmetr/metaprogramming-ruby">книжка</a> на эту тему. Но занимаясь именно динамическим, а не статическим, метапрограммированием, следует понимать что это не совсем одно и то же &ndash; динамическое программирование это серьезное заигрывание с концепциями а-ля <code>ExpandoObject</code>, то есть манипуляция типов на этапе исполнения &ndash; нечто, что после безумной головной боли можно реализовать и на <a href="http://www.mono-project.com/Cecil">Mono.Cecil</a>. В то же время, статическое метапрограммирование &ndash; это возможность расширять не тип, а <em>компилятор</em>. Тем самым, сравнивать эти два подхода не очень уместно, и то что Роб Конери хочет &ldquo;метапрограммирование сегодня и сейчас&rdquo; &ndash; это лично <em>его</em> представление о том, что это такое, и мне кажется он смешивает краски.</p>
<p>Следует заметить, что у IronRuby в отличии от IronPython нет поддержки в Visual Studio. Поэтому придется использовать или SharpDevelop или (сюрприз!!!) IntelliJ IDEA/<a href="http://www.jetbrains.com/ruby/index.html">RubyMine</a>. При этом вы получите IntelliSense для ваших собственных конструкций, но его у вас не будет для различных .Net-библиотек.</p>
<h3>Заключение</h3>
<p>Прежде всего &ndash; да &ndash; существуют и другие языки в стеке .Net, например <a href="http://ironscheme.codeplex.com/">IronScheme</a>. Но чем дальше мы отходим от &ldquo;канона&rdquo; тем дальше мы и от обычного бизнес-программирования, а следовательно каким бы элегантным не был тот или иной язык, можно с уверенностью говорить что без участия комьюнити, uptake такого языка будет равен нулю. Примером может служить язык <a href="http://nemerle.org/Main_Page">Nemerle</a>, которого, несмотря на наличие интересных (но ни в коей мере не революционных) идей, ожидает та же участь что и Boo.</p>
<p>Проблемы со сторонними языками можно охарактеризовать примерно так:</p>
<ul>
<li>Бизнес не примет их без поддержки Microsoft</li>
<li>Количество людей, хорошо знающих эти языки мало&#x301;, а значит те сотрудники что у вас их знают рискуют стать незаменимыми</li>
<li>Клиент вряд ли согласиться на проект на языке, особенно если саппорт он хочет получить не у вас, а где-то еще</li>
<li>Инструментарий пока не очень готов &ndash; это касается даже таких языков как F#</li>
<li>Реально не хватает success stories, которые четко показывают бенефиты</li>
</ul>
<p>В заключение этой статьи я призываю всех .Net-разработчиков порадоваться, что у нас есть такой гибкий язык как C#, который удовлетворяет все 100% (да-да, именно 100%) наших потребностей. С другой стороны, никто не мешает экспериментировать &ndash; главное чтобы ваши эксперименты потом попадали в production, а то иначе это все академизм и теоретизирование. Удачи! <img src="http://nesteruk.org/projects/visitlogger/log" alt="" /></p>
<p>
  <strong>Update 1</strong> Как подсказал коллега <a href="http://twitter.com/butaji" title="butaji's posts on twitter">@butaji</a>, DLR полезен тем что позволяет легко &ldquo;скриптовать&rdquo; ваши .Net-приложения, то есть фактически получать возможность скриптинга ваших .Net-конструктов без использования COM Automation или еще более кустарных извратов. Это конечно огромный плюс в тех ситуациях когда это действительно нужно, особенно если учесть альтернативы (создание всяких проприетарных DSL и т.п.).
</p>
<p>
  <strong>Update 2</strong> Судя по всему, с DLR-языками, в частности с IronRuby, все <a href="http://blog.jimmy.schementi.com/2010/08/start-spreading-news-future-of-jimmy.html" title="http://blog.jimmy.schementi.com/2010/08/start-spreading-news-future-of-jimmy.html">достаточно плохо</a>.
</p>
<p>
  <strong>Update 3</strong><br />
  Судя по всему, огромным приемуществом DLR являются ситуации, когда нам нужно постоянно подтачивать нашу программу уже после компиляции. Коллега <a href="http://twitter.com/butaji" title="butaji's posts on twitter">@butaji</a> уже это упомянул, но мне пока это не попадалось как вопиющий юз-кейс. А теперь я даже обзавелся компонентом который делает синтактическую подсветку. Буду встраивать; посмотрим что получится.</p>
<br />Filed under: <a href='http://nesteruk.wordpress.com/category/net/'>.NET</a>, <a href='http://nesteruk.wordpress.com/category/c/'>C#</a>, <a href='http://nesteruk.wordpress.com/category/net/f/'>f#</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/nesteruk.wordpress.com/490/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/nesteruk.wordpress.com/490/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/nesteruk.wordpress.com/490/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/nesteruk.wordpress.com/490/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/nesteruk.wordpress.com/490/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/nesteruk.wordpress.com/490/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/nesteruk.wordpress.com/490/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/nesteruk.wordpress.com/490/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/nesteruk.wordpress.com/490/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/nesteruk.wordpress.com/490/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/nesteruk.wordpress.com/490/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/nesteruk.wordpress.com/490/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/nesteruk.wordpress.com/490/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/nesteruk.wordpress.com/490/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=490&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://nesteruk.wordpress.com/2010/08/02/dot-net-polyglot-programming/feed/</wfw:commentRss>
		<slash:comments>54</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f01cbd2238e2a78e4c43fa596f51d6a1?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=G" medium="image">
			<media:title type="html">nesteruk</media:title>
		</media:content>

		<media:content url="http://nesteruk.org/projects/visitlogger/log" medium="image" />
	</item>
		<item>
		<title>C# Zen Coding уже на F#</title>
		<link>http://nesteruk.wordpress.com/2010/03/05/c-zen-coding-%d1%83%d0%b6%d0%b5-%d0%bd%d0%b0-f/</link>
		<comments>http://nesteruk.wordpress.com/2010/03/05/c-zen-coding-%d1%83%d0%b6%d0%b5-%d0%bd%d0%b0-f/#comments</comments>
		<pubDate>Fri, 05 Mar 2010 19:45:39 +0000</pubDate>
		<dc:creator>Dmitri</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[f#]]></category>

		<guid isPermaLink="false">http://nesteruk.wordpress.com/?p=271</guid>
		<description><![CDATA[В моем предыдущем посте, где я описал идею CSharpZen, я пообещал две вещи &#8211; дописать расширение для Visual Studio 2010, а также записать вебкаст того, как это можно использовать. Но пока я дописывал код для трансформатора, мне снова показалось что код может стать более понятным если его переписать на F#. И понеслось&#8230; Почему опять F#? [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=271&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>В моем <a href="http://nesteruk.wordpress.com/2010/03/03/%D1%81%D0%BE%D0%BA%D1%80%D0%B0%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9-%D0%B3%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80-c-%D0%B2-%D1%81%D1%82%D0%B8%D0%BB%D0%B5-zen-coding/">предыдущем посте</a>, где я описал идею CSharpZen, я пообещал две вещи &ndash; дописать расширение для Visual Studio 2010, а также записать вебкаст того, как это можно использовать. Но пока я дописывал код для трансформатора, мне снова показалось что код может стать более понятным если его переписать на F#. И понеслось&hellip;</p>
<p>Почему опять F#? Я уже <a href="http://nesteruk.wordpress.com/2010/02/18/%D0%BE%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0-%D1%81%D1%82%D1%80%D0%BE%D0%BA-%D1%81%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5-c-%D0%B8-f/">писал</a> о том что строковые трансформации лучше делать на F# и что они хоть и работают медленнее (хотя Zen Coding вообще написан на питоне и использует DLR), но код легче понимать. А у меня еще появляется работа с массивами неопределенной длины (например при обработке свойств может быть <code>p:sb x</code> а может <code>p:new sb x</code>) что весьма неплохо ложится на pattern matching.</p>
<p>В этом посте я хочу пройтись по проблемам прошлого поста и показать как в очередной раз F# позволяет нам производить аналогичный код но в более понятной форме.</p>
<h3>Проблема использования метаданных</h3>
<p>Помните как мы определили метаданные для видимости как аттрибуты? Так вот, этот подход хоть и казался полезным (держим метаданные рядом с полями), но на самом деле был жутко неудобным. В F# такой подход не сработает. Вместо этого, мы определяем перечисление отдельно&hellip;</p>
<p><pre class="brush: fsharp;">type Visibility =
| Public
| Internal
| ProtectedInternal
| Protected
| Private
</pre>
<p>&hellip;а метаданные определяем как массив кортежей которые содержат все нужные нам данные.</p>
<p><pre class="brush: fsharp;">type Constants =
  static member visibilityTokens =
    [|(Visibility.Public, &quot;+&quot;, &quot;public&quot;); 
      (Visibility.Internal, @&quot;\&quot;, &quot;internal&quot;);
      (Visibility.ProtectedInternal, @&quot;/\&quot;, &quot;protected internal&quot;);
      (Visibility.Protected, @&quot;/&quot;, &quot;protected&quot;);
      (Visibility.Private, &quot;-&quot;, &quot;private&quot;)|]
  ...
</pre>
<p>Теперь для поиска конретных элементов метаданных мы просто используем <code>Array.find</code> по кортежам. Например, та же функция вытаскивания <code>Visibility</code> из начала строки теперь выглядит вот так:</p>
<p><pre class="brush: fsharp;">static member getVisibilityToken (entry:String) =
  try
    let idx = Constants.visibilityTokens |&gt; Array.findIndex(fun (a,b,c) -&gt; entry.StartsWith(b))
    let (a, b, c) = Constants.visibilityTokens.[idx]
    (a, entry.Replace(b, String.Empty))
  with
    :? KeyNotFoundException -&gt;
       let (a, b, c) = Constants.visibilityTokens.[0]
       (a, entry)
</pre><br />
<h3>Свойства расширения</h3>
<p>F# знаменит тем что можно делать не только extension methods но и extension properties. Это хорошо. Единственная проблема это то, что методы расширения приходится класть в отдельный модуль, а я это не люблю &ndash; предпочитаю держать все в одном файле и типе, безо всяких там модулей. А тут не получилось &ndash; пришлось описывать все отдельно:</p>
<p><pre class="brush: fsharp;">module Extensions =
  type String with
    member x.Cap =
      let parts = x.Split([|'_'|], StringSplitOptions.RemoveEmptyEntries)
      parts |&gt; Array.map(fun f -&gt; Char.ToUpper(f.[0]).ToString() +
               (if f.Length &gt; 1 then f.Substring(1) else String.Empty))
            |&gt; String.Concat
</pre>
<p>Этот код вообще эклектичен &ndash; взять хотя бы «заинлайненый» блок <code>if-then-else</code> прямо внутри <code>map</code>-а. Это что-то. И это что-то не очень-то читабельно. Хотя кому как.</p>
<h3>Сборщик кода</h3>
<p>Помните <code>CodeBuilder</code>? Так вот, решил я его на F# переписать. В чем-то он лучше, но вот навязчивое желание F# сохранять результаты вычислений или их игнорировать весьма раздражает. Код ниже дает качественно новое определение фразе «послать в игнор»:</p>
<p><pre class="brush: fsharp;">type CodeBuilder(spaces) =
  let mutable indent = 0
  let sb = StringBuilder()
  member x.IndentText =
    String.Empty.PadRight(spaces * indent)
  member x.Indent() =
    indent &lt;- indent + 1
    x
  member x.Unindent() =
    indent &lt;- Math.Max(0, indent - 1)
    x
  member x.Append(text:String) =
    sb.Append(x.IndentText).Append(text) |&gt; ignore
    x
  member x.AppendLine(text:String) =
    sb.Append(x.IndentText).AppendLine(text) |&gt; ignore
    x
  override x.ToString() =
    sb.ToString()
</pre><br />
<h3>Неадекват с мутабельностью и null</h3>
<p>Кажется я уже об этом как-то писал. Суть в том, что F# лютой ненавистью ненавидит мутабельность и <code>null</code>. То есть он их поддерживает, но чтобы их использовать&hellip; короче Жигули тоже поддерживают езду по дороге, скажем так. Но то, что я вам сейчас покажу это вообще лебединая песня.</p>
<p>Для начала напомню что для того чтобы значение можно было изменять, его нужно пометить как <code>mutable</code> и для записи использовать оператор <code>&lt;-</code>:</p>
<p><pre class="brush: fsharp;">let mutable x = 0
x &lt;- 2
</pre>
<p>Все бы хорошо, но это нелегитивно внутри паттерн-мэтчинга:</p>
<p><pre class="brush: fsharp;">match myDrug with
| Vicodin -&gt; x &lt;- 2 // Вам нельзя.
</pre>
<p>Суть в том, что при таком раскладе вы больше не можете пользоваться «обычным» подходом, вам нужно пересесть на синтаксис который использует &lsquo;ref cells&rsquo;. Я даже это переводить не буду, <a href="http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!677.entry">почитать можно тут</a>. Если коротко, то используется совсем другой синтаксис:</p>
<p><pre class="brush: fsharp;">let x = ref 0
x := !x + 1
</pre>
<p>А теперь представьте что вам нужно делать то же самое но для nullable-полей. Помните мои меременные <code>context</code> и <code>rootContext</code>? Вот-вот. Они nullable, и как вы помните, F# не поддерживает эту парадигму (точнее поддерживает, но через одно место) в результате чего мне не написать подобное:</p>
<p><pre class="brush: fsharp;">let context:IVisitable = null     // fail
let context:IVisitable = ref null // epic fail
</pre>
<p>На самом деле, F# имеет свою парадигму <code>option&lt;'t&gt;</code> для подобных вещей. Только она тоже сработает только после дозы шаманства:</p>
<p><pre class="brush: fsharp;">let context : IVisitable option = ref none // FAIL
let context : Ref&lt;IVisitable option&gt; = ref None // OK, finally *phew*
</pre>
<p>Имхо, это просто <strong>умопомрачительно</strong>. Вы можете себе представить индийскую аутсорсинговую компанию, в которой программисты будут разбираться с подобным кодом? Я &ndash; нет.</p>
<h3>Мутабельность переменных</h3>
<p>И еще про мутабельность. В свое время я <a href="http://www.gotdotnet.ru/blogs/nesteruk/4296/">написал статейку</a> про то как я использую F# DSL для оценки проектов. Так вот, там фигурирует такая неопрятная вещь как публичное поле-список:</p>
<p><pre class="brush: fsharp;">[&lt;DefaultValue&gt;] val mutable Groups : Group list
</pre>
<p>Для отказа от <code>List&lt;T&gt;</code> реальных причин нет &ndash; зато есть желание делать все чуть более «функционально». За это мы платим тем, что при добавлении элемента, список полностью заменяется. Не знаю стоимость этой операции, возможно не так дорого, но выглядит странно.</p>
<p><pre class="brush: fsharp;">x.Groups &lt;- newGroup :: x.Groups
</pre>
<p>Зато теперь я могу показать как выглядит определение пространства имен:</p>
<p><pre class="brush: fsharp;">type Namespace() =
  [&lt;DefaultValue&gt;] val mutable Name : String
  [&lt;DefaultValue&gt;] val mutable Classes : Class list
  ...
</pre>
<p>Вот вам и ужасный синтаксис во всей «красе». Зато все работает. Главные плюсы вообщем-то не здесь, а в паттерн-матчинге самого парсера.</p>
<h3>Паттерн матчинг рулит</h3>
<p>Вот собственно где все самое интересное. То, как организован паттер-матчинг в F# позволяет нам лаконично (ненавижу это слово!) описывать разные условия. Вот небольшой пример:</p>
<p><pre class="brush: fsharp;">// split the new classifier and match against it
let parts = x.splitToList(newClassifier, ':')
match parts with
| (* NAMESPACE *) &quot;ns&quot; :: tail -&gt;
  let ns = Namespace()
  match tail with
  | name :: _ -&gt; ns.Name &lt;- name
  | _ -&gt; ns.Name &lt;- co.DefaultNamespace
  let nsv = ns :&gt; IVisitable
  context := Some nsv
  setRootContext nsv
</pre>
<p>Получив классификатор, мы можем проверить, есть у него имя или нет. При этом избыточные элементы мэтчинга отбрасываются. Но если нам вдруг понадобится иметь «сложные» классификаторы вроде <code>a:b:c</code> то добавить их поддержку проще простого &ndash; это всего лишь еще один кейс:</p>
<p><pre class="brush: fsharp;">| name :: somethingElse :: -&gt; // что-то умное
</pre>
<p>Зато гибкость теряется в весьма неудобной (излишней) конверсии <code>Namespace</code> в <code>Visitable</code>. О да, кстати, давайте я быстренько покажу как работают интерфейсы в F#.</p>
<h3>Интерфейсы</h3>
<p>У нас всего один интерфейс &ndash; <code>IVisitable</code>. Но в F# ключевое слово <code>interface</code> используется только когда вы <em>реализуете</em> интерфейс в отдельном классе. Если вы просто хотите объявить интерфейс, то делаете класс с абстрактными объявлениями методов:</p>
<p><pre class="brush: fsharp;">type IVisitable = 
  abstract Visit : CodeBuilder * ConversionOptions -&gt; unit
</pre>
<p>Реализация интерфейса всегда явная, т.е. вы прописываете у какого интерфейса и что вы реализуете. Вот пример полного определения класса <code>Namespace</code>:</p>
<p><pre class="brush: fsharp;">type Namespace() =
  [&lt;DefaultValue&gt;] val mutable Name : String
  [&lt;DefaultValue&gt;] val mutable Classes : Class list
  interface IVisitable with
    member x.Visit(cb, co) =
      cb.Append(&quot;namespace &quot;).Append(x.Name.Cap).AppendLine(&quot; {&quot;).Indent() |&gt; ignore
      List.iter(fun c -&gt; (c :&gt; IVisitable).Visit(cb, co)) |&gt; ignore
      cb.Unindent().AppendLine(&quot;}&quot;) |&gt; ignore
</pre><br />
<h3>Многоуровневый паттерн-мэтчинг</h3>
<p>В парсере ни много ни мало 5 (пять!!!) уровней паттерн-мэтчинга. Вот четыре их них которые задействованы в разборе определения класса:</p>
<p><pre class="brush: fsharp;">| (* CLASS *) &quot;c&quot; :: tail -&gt;
  // get the name
  match tail with
  | [] -&gt; raise(Exception(&quot;Class is missing a name&quot;))
  | name :: _ -&gt;
    let c = Class(name, visibility)
    let cv = c :&gt; IVisitable
    context := Some cv
    match !rootContext with
    | Some(x) -&gt;
      match x with
      | :? Namespace as ns -&gt; ns.Classes &lt;- c :: ns.Classes
      | _ -&gt; ()
    | None -&gt; ()
    setRootContext cv
</pre>
<p>Поскольку класс обязательно нужно положить внутрь простанства имен (если таковое имеется), нам приходится делать двойное сравнение с <code>Option&lt;Namespace&gt;</code> &ndash; сначала нужно проверить что это действительно <code>Some(x)</code>, а потом проверить что тип совпадает (мало ли&hellip;). Проблема в том, что само по себе <code>Ref&lt;Namespace option&gt;</code> не ведет себя полиморфно и одним оператором <code>:?</code> в мэтче не обойтись. А жаль. Было бы удобно.</p>
<h3>Реализация свойств</h3>
<p>Ну и последний вынос мозга на сегодня. Помните я говорил что в свойство можно вставлять слово <code>new</code>? Интересно, но pattern matching возвращает значения. В контексте свойства мы можем взять свойство <code>args</code> (хвост наших токенов) и вернуть из него кортеж!</p>
<p><pre class="brush: fsharp;">let (name, mustInit, skipCount) = match args with
| name :: _ when typeName.IsNotAClassifier -&gt; (name, false, 1)
| &quot;new&quot; :: name :: _ when typeName.IsNotAClassifier -&gt; (name, false, 2)
| _ -&gt; raise(Exception(&quot;Property name is missing&quot;))
let p = Property(name, visibility, typeName, mustInit)
</pre>
<p>Угадайте зачем нужен <code>skipCount</code>. На самом деле он нужен чтобы «отмотать» несколько первых элементов списка <code>args</code> для последующей обработки. Сама отмотка реализована достаточно брутально:</p>
<p><pre class="brush: fsharp;">let rec skipElements count theList =
  match theList with
  | h :: t when count &gt; 0 -&gt; skipElements (count-1) t
  | _ -&gt; theList
</pre>
<p>Думаете что <code>theList</code> должен быть первым параметром? А вот и нет. Если определить его именно так, можно получить частичное применение «огрызка» нашего списка токенов:</p>
<p><pre class="brush: fsharp;">args |&gt; skipElements 2 |&gt; buildStructure
</pre>
<p>Вот собственно зачем нам был нужен <code>skipElements</code>.</p>
<h3>Заключение</h3>
<p>Интересно почему обработка строк и массивов всегдя тянет меня на F# &ndash; ведь в нем море проблем в которых можно утонуть и, что самое главное, когда пишешь код в нефункциональном стиле, написание занимает в 4 раза больше времени чем на C#. Возможно имеет больший смысл писать обработку на кастомном DSL, предназначенном именно для таких задач. А на чем писать такой DSL? Возможно на F#.</p>
<p>Я обновил проект, так что если интересно, весь описанный в этом посте F# код лежит <a href="http://bitbucket.org/nesteruk/csharpshorthand/">в репозитарии</a>.</p>
<p>Интересное наблюдение: после нескольких часов мучений с F# чувствую себя не героем а идиотом. К чему бы это?&nbsp;■</p>
<br />Filed under: <a href='http://nesteruk.wordpress.com/category/c/'>C#</a>, <a href='http://nesteruk.wordpress.com/category/net/f/'>f#</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/nesteruk.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/nesteruk.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/nesteruk.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/nesteruk.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/nesteruk.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/nesteruk.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/nesteruk.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/nesteruk.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/nesteruk.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/nesteruk.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/nesteruk.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/nesteruk.wordpress.com/271/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/nesteruk.wordpress.com/271/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/nesteruk.wordpress.com/271/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=271&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://nesteruk.wordpress.com/2010/03/05/c-zen-coding-%d1%83%d0%b6%d0%b5-%d0%bd%d0%b0-f/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f01cbd2238e2a78e4c43fa596f51d6a1?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=G" medium="image">
			<media:title type="html">nesteruk</media:title>
		</media:content>
	</item>
		<item>
		<title>Обработка строк &#8211; сравнение C# и F#</title>
		<link>http://nesteruk.wordpress.com/2010/02/18/%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%ba%d0%b0-%d1%81%d1%82%d1%80%d0%be%d0%ba-%d1%81%d1%80%d0%b0%d0%b2%d0%bd%d0%b5%d0%bd%d0%b8%d0%b5-c-%d0%b8-f/</link>
		<comments>http://nesteruk.wordpress.com/2010/02/18/%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%ba%d0%b0-%d1%81%d1%82%d1%80%d0%be%d0%ba-%d1%81%d1%80%d0%b0%d0%b2%d0%bd%d0%b5%d0%bd%d0%b8%d0%b5-c-%d0%b8-f/#comments</comments>
		<pubDate>Thu, 18 Feb 2010 10:32:42 +0000</pubDate>
		<dc:creator>Dmitri</dc:creator>
				<category><![CDATA[f#]]></category>

		<guid isPermaLink="false">http://nesteruk.wordpress.com/?p=216</guid>
		<description><![CDATA[<p>У меня есть приложение, с помощью которого я пишу статьи вроде этой. Называется это приложение TypograFix и суть его в том, что оно помогает мне готовить тексты к публикации в итернете. Одна из фич этого приложения &#8211; правильное типографирование текста, которое делается с помощью парсера, который обходит текст и производит нужные замены (например, меняет <code>--&#62;</code> на <code>--&#38;gt;</code> что до читателя доходит как &#8594;. До сегоняшнего момента, код который делает эту замену был написал на C#. Читается он, прямо скажем, сложно. В этом посте я хочу показать как можно написать подобный парсер на F#, и в чем отличия подобного подхода.</p><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=216&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>У меня есть приложение, с помощью которого я пишу статьи вроде этой. Называется это приложение TypograFix и суть его в том, что оно помогает мне готовить тексты к публикации в итернете. Одна из фич этого приложения &ndash; правильное типографирование текста, которое делается с помощью парсера, который обходит текст и производит нужные замены (например, меняет <code>--&gt;</code> на <code>--&amp;gt;</code> что до читателя доходит как &rarr;. До сегоняшнего момента, код который делает эту замену был написал на C#. Читается он, прямо скажем, сложно. В этом посте я хочу показать как можно написать подобный парсер на F#, и в чем отличия подобного подхода.</p>
<p>Было бы здорово, если бы для замены определенных последовательностей в тексте можно было использовать <code>String.Replace()</code> или на худой конец <code>Regex.Replace()</code>. К сожалению, для HTML это не вариант &ndash; вам нужно знать не только местоположение символа, но также и <em>контекст</em>, то есть общую ситуацию в которой парсер находится в данный момент. Например, если вы в теге <code>&lt;code&gt;</code>, то естественно что замены кавычек делать не стоит. Более того, подход при котором в конце получается объектная модель (например с использованием Microsoft.mshtml или другого парсера) мне тоже не подходит т.к. я люблю периодически отказываться от тегов в пользу символьной репрезентации &ndash; например я использую символы <code>{{</code> и <code>}}</code> для разделения блоков кода.</p>
<p>Так вот, к чему это я? На данный момент, мой подход можно проиллюстрировать вот так:</p>
<p><pre class="brush: cpp;">public string Transform(string text)
{
  var sb = new StringBuilder();
  for (int i = 0; i &lt; text.Length; ++i)
  {
    switch (text[i])
    {
      case '-':
        if (text[i+1] == '-' &amp;&amp; text[i+2] == '&gt;')
        {
          sb.Append(&quot;&amp;rarr;&quot;);
          i += 2;
          continue;
        }
        else goto default;
    }
  }
}
</pre>
<p>Согласитесь, выглядит не очень. У меня сейчас этот метод (с тысячью всяких примочек) занимает несколько сотен строк кода, и абсолютно не рефакторится по причине завязки на разные состояния взаимодействия (большинство из которых вообще представляют из себя поля на уровне класса).</p>
<p>Как нам может помочь F#? В принципе, он может дать нам возможность лучше разделить наш код на отдельные кейсы (например &ldquo;кавычки&rdquo; или &ldquo;тире и прочерки&rdquo;) которые нам нужно обрабатывать. Единственная проблема &ndash; нам придется отказаться от строк в пользу списков, что безусловно ударит по производительности. Моя задача &ndash; типичный пример того, как можно отдать чуть-чуть производительности на откуп читабельности кода.</p>
<p>Итак, давайте начнем с основ. Поскольку строка &ndash; это последовательность символов (ну, по крайней мере так считает F#), ее нужно для начала преобразовать в список. Делается это достаточно просто:</p>
<p><pre class="brush: fsharp;">let x = &quot;Hello&quot;
let y = Seq.toList(x)
Console.WriteLine(List.length y) // пишет 5
Console.WriteLine(List.toArray y) // пишет Hello
</pre>
<p>Пример выше иллюстрирует текст как список (именно список, а не последовательность &ndash; sequence &ndash; букв), а также возможность получения обратно строки (в примере выше &ndash; как <code>char[]</code>, но из этого можно и <code>System.String</code> построить). Если у вас возникло удивление от названия метода <code>toList()</code> (еще недавно ведь было <code>to_list()</code>, не так ли?), вам нужно обновить свой компилятор F#.</p>
<p>Имея список, можно делать его обход. Основной порыв в данном случае &ndash; начать &ldquo;подсматривать&rdquo; элементы списка и соответственно заменять их по мере необходимости. Примерно вот так:</p>
<p><pre class="brush: fsharp;">let rec Transform text =
  match text with
  | '-' :: '-' :: '&gt;' :: t -&gt; Seq.toList(&quot;&amp;rarr;&quot;) @ Transform t
  | h :: t -&gt; h :: Transform t
  | [] -&gt; []
 
let input = &quot;a --&gt; b&quot;
let result = input |&gt; Seq.toList |&gt; Transform |&gt; List.toArray
Console.WriteLine(new string(result)) // пишет a --&amp;rarr; b
</pre>
<p>Такой подход позволяет нам четко выделять все кейсы в отдельные строчки и при этом нам не нужно использовать злое слово <code>goto</code>. Почему? Для этого нам сначала нужно разобраться с контекстом. Что такое контекст? Это по сути дела некий набор состояний &ndash; один большой мутабельный god object который содержит различные состояния определенной трансформации. Есть два таких состояния:</p>
<ul>
<li>
<p>Собственно контекст, т.е. стек (<code>Stack&lt;string&gt;</code>) которые содержит те теги, в которых парсер находится на данный момент.</p>
</li>
<li>
<p>Набор переменных, которые символизируют различные внутренние состояния, не связанные с тегами. Например, <code>bool</code> флажок, описывающий, открыты ли у нас кавычки.</p>
</li>
</ul>
<p>Вообщем-то, представление в данном случае не важно. Важно лишь то, что все специальные случаи вроде &ldquo;обрабатывать как есть если мы в теге <code>&lt;code&gt;</code>&rdquo; можно с помощью очень полезного для pattern matching-а ключевого слова <code>where</code>:</p>
<p><pre class="brush: fsharp;">// подразумевается, что этот метод стал вложенным в другой метод,
// в котором объявлены элементы контекста
let rec Transform text =
  match text with
  | h :: t when context.CannotReplace -&gt; h :: Transform t
  ...
</pre>
<p>Но это еще не все. В нашем коде фигурирует паттерн <code>'-' :: '-' :: '&gt;'</code>, а писать такое, согласитесь, весьма неудобно. Легче было бы написать <code>"--&gt;"</code>, но к сожалению такой подход не сработает, т.к. просто вставить сюда строку нельзя. Зато можно определить дополнительную функцию (partial pattern matching) которая делала бы проверку именно &ldquo;префикса&rdquo; текущей строки:</p>
<p><pre class="brush: fsharp;">let rec (|Prefix|_|) s l =
  if s = &quot;&quot; then
    Some(Prefix l)
  else
    match l with
    | h :: (Prefix (s.Substring(1)) xs) when h = s.[0] -&gt; Some(Prefix xs)
    | _ -&gt; None
</pre>
<p>Используя эту вспомогательную функцию, мы можем писать подобый код:</p>
<p><pre class="brush: fsharp;">let rec Transform text =
  match text with
  | Prefix &quot;--&gt;&quot; t -&gt; Seq.toList(&quot;&amp;rarr;&quot;) @ Transform t
  | h :: t -&gt; h :: Transform t
  | [] -&gt; []
</pre>
<p>Этот подход несколько понизил читаемость кода т.к. теперь перед каждым случаем использования длинного префикса (а для однобуквенных префиксов он не особо нужен) теперь фигурирует слово <code>Prefix</code>. Конечно я мог бы выбрать слово покороче, но суть не в этом, а в том что это слово есть, и следовательно оно отвлекает.</p>
<p>Давайте посмотрим на более сложный пример в плане паттерн-матчинга &ndash; попробуем заменить <code>x</code> на <code>&amp;times;</code> только в тех случаях, когда буква <code>x</code> расположена между двумя числами (например в 2&times;2). Как это сделать? Примерно вот так:</p>
<p><pre class="brush: cpp;">let rec Transform text =
  match text with
  | Prefix &quot;--&gt;&quot; t -&gt; Seq.toList(&quot;&amp;rarr;&quot;) @ Transform t
  | a :: 'x' :: c :: t when Char.IsDigit(a) &amp;&amp; Char.IsDigit(c) -&gt;
      a :: Seq.toList(&quot;&amp;times;&quot;) @ c :: Transform t
  | h :: t -&gt; h :: Transform t
  | [] -&gt; []
</pre>
<p>В примере выше сразу несколько &ldquo;усложнений&rdquo; ситуации &ndash; мы опять используем длинный список элементов в паттерне, но помимо этого мы используем <code>when</code> а также делаем достаточно неинтуитивную конкатенацию списков. Неинтуитивную потому, что обычно есть соблазн писать либо <code>::</code> либо <code>@</code> для соединения списков, а тут получается что мы соединяем два списка с головами <code>a</code> и <code>c</code> соответственно. Ну да ладно, не проблема, ведь все работает.</p>
<p>До сих пор мы ограничивались ситуациями, в которых паттерн имел фиксированную длину, но это не всегда так. Например, для производства заметок (refernces) я использую квадратные скобки (<code>[вот так]</code>), и тем самым нужно отловить начало и конец ссылки для того чтобы поместить содержимое в буфер. В последствии это содержимое добавляется в соответствующую секцию, но я в примере ниже опущу этот момент ради экономии места.</p>
<p><pre class="brush: fsharp;">let Transform text:String =
  let input = text |&gt; Seq.toList
  let buffer = new StringBuilder()
  let buffering = ref false
  let rec TransformImpl text =
    match text with
    | ']' :: t when !buffering -&gt;
      Console.WriteLine(buffer.ToString());
      // обрабатываем ссылку, а потом...
      buffering := false
      TransformImpl t
    | h :: t when !buffering -&gt;
      buffer.Append(h:char)
      TransformImpl t
    | '[' :: t -&gt;
      buffer.Clear()
      buffering := true
      TransformImpl t
    | Prefix &quot;--&gt;&quot; t -&gt; Seq.toList(&quot;&amp;rarr;&quot;) @ TransformImpl t
    | a :: 'x' :: c :: t when Char.IsDigit(a) &amp;&amp; Char.IsDigit(c) -&gt;
        a :: Seq.toList(&quot;&amp;times;&quot;) @ c :: TransformImpl t
    | h :: t -&gt; h :: TransformImpl t
    | [] -&gt; []
  new string(input |&gt; TransformImpl |&gt; List.toArray)
</pre>
<p>В примере выше, нам пришлось воспользоваться ключевым словом <code>ref</code> для того чтобы сделать переменную <code>buffering</code> изменяемой внутри нашего паттерн-матчинга. Причем в данном случае, мы не могли воспользоваться ключевым словом <code>mutable</code> и оператором <code>&lt;-</code>, а вместо этого нам пришлось использовать <code>ref</code> (т.е. создание ссылочного типа), а также операторами <code>:=</code> для присваивания и <code>!</code> для считывания. Увы, в данном примере F# не добавил читабельности.</p>
<p>Такой подход, на самом деле неидеален &ndash; нам пришлось разнести &ldquo;начало&rdquo; и &ldquo;конец&rdquo; паттерна на разные обработчики, которые в последствии даже не смогут стоять рядом друг с другом. Впрочем, для того чтобы получить &ldquo;конечную&rdquo; строку которую нам нужно выдернуть из списка, можно всего-навсего определить очередной &ldquo;активный паттерн&rdquo;, что я возможно когда-нибудь и сделаю.</p>
<p>Итак, попробую прорезюмировать плюсы и минусы данного подхода. Сначала о хорошем:</p>
<ul>
<li>
<p>Больше не нужно писать большой <code>switch</code>-блок в котором используется злое ключевое слово <code>goto</code>.</p>
</li>
<li>
<p>Если длина ожидаемого паттерна фиксирована, его можно описать с помощью обычной строки.</p>
</li>
</ul>
<p>С другой стороны, к сожалению, есть ряд минусов в плане полезности F#:</p>
<ul>
<li>
<p>Паттерны с неорганиченной длинной сложнее разбирать. В C# мы бы могли использовать поиск по строке и <code>Substring()</code> для выделения определенного элемента.</p>
</li>
<li>
<p>У паттернов должен быть определенный порядок исполнения, нарушив который мы ломаем всю систему. Тем самым, имеет место ситуация, в которой схожие по предназначению паттерны находятся далеко друг от друга лишь потому, что некоторые из них обязательно должны быть в начале разбора. N.b.: я не исключаю, что можно выделять выборку целых блоков внутри одного паттерн-матчинга путем создания дополнительных вложенных функций.</p>
</li>
<li>
<p>Использование F# не добавило задаче той системности, которая могла бы сделать код полностью читаемым. Все равно нужно писать документацию к разным кейсам, точно так же как я это делал в C#.</p>
</li>
<li>
<p>Существуют определенные проблемы с мутабельностью, которые периодически будут появляться со временем разростания &ldquo;контекста&rdquo; разбора.</p>
</li>
</ul>
<p>Надеюсь что в этом примере мне удалось наглядно продемонстрировать альтернативный подход к разбору строк в ситуации, когда обычные парсеры не работают. Не уверен что стоит рекоммендовать этот подход для всех типов задач, т.к. обычно для парсеров скорость разбора является критичной. Для меня к счастью она таковой не является, поэтому я продолжу использовать F#. Посмотрим, к чему меня это приведет.&nbsp;■</p>
<br />Filed under: <a href='http://nesteruk.wordpress.com/category/net/f/'>f#</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/nesteruk.wordpress.com/216/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/nesteruk.wordpress.com/216/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/nesteruk.wordpress.com/216/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/nesteruk.wordpress.com/216/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/nesteruk.wordpress.com/216/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/nesteruk.wordpress.com/216/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/nesteruk.wordpress.com/216/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/nesteruk.wordpress.com/216/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/nesteruk.wordpress.com/216/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/nesteruk.wordpress.com/216/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/nesteruk.wordpress.com/216/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/nesteruk.wordpress.com/216/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/nesteruk.wordpress.com/216/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/nesteruk.wordpress.com/216/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=216&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://nesteruk.wordpress.com/2010/02/18/%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%ba%d0%b0-%d1%81%d1%82%d1%80%d0%be%d0%ba-%d1%81%d1%80%d0%b0%d0%b2%d0%bd%d0%b5%d0%bd%d0%b8%d0%b5-c-%d0%b8-f/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f01cbd2238e2a78e4c43fa596f51d6a1?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=G" medium="image">
			<media:title type="html">nesteruk</media:title>
		</media:content>
	</item>
		<item>
		<title>Создание DSL на языке F#</title>
		<link>http://nesteruk.wordpress.com/2009/08/29/dsl-fsharp/</link>
		<comments>http://nesteruk.wordpress.com/2009/08/29/dsl-fsharp/#comments</comments>
		<pubDate>Sat, 29 Aug 2009 07:14:30 +0000</pubDate>
		<dc:creator>Dmitri</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[dsl]]></category>
		<category><![CDATA[f#]]></category>

		<guid isPermaLink="false">http://nesteruk.wordpress.com/?p=614</guid>
		<description><![CDATA[Хочу представить сообществу перевод моей статьи на CodeProject, в которой я описываю процесс создания DSLей с использованием языка F#. Введение Если честно, мне уже изрядно поднадоели разговоры о DSLях в чисто академическом ключе. Хочется увидеть конкретный пример того, как это счастье используется «в продакшн». Да и вообще, саму концепцию можно объяснить и реализовать намного более [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=614&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Хочу представить сообществу перевод моей статьи на CodeProject, в которой я описываю процесс создания DSLей с использованием языка F#.</p>
<h3>Введение</h3>
<p>Если честно, мне уже изрядно поднадоели разговоры о DSLях в чисто академическом ключе. Хочется увидеть конкретный пример того, как это счастье используется «в продакшн». Да и вообще, саму концепцию можно объяснить и реализовать намного более доходчиво и прямолинейно чем делают авторы таких фреймворков как Oslo или MPS. Собственно тут я как раз и хочу показать решение которое вовсе не академическое а именно производственное, и служит конкретным целям.</p>
<p>Начнем с того, что обсудим что же такое DSL. DSL &ndash; доменно специфичный язык &ndash; то есть способ описания той или иной предметной специфики (которая часто связана с конкретной индустрией) с помощью такого языка, который могут понять не только разработчики, но и эксперты в предметной области. Важно в этом языке то, что те кто его используют не должны думать о фигурных скобочках, точках с запятой и прочих прелестях программирования. То есть у них должна быть возможность писать на «простом английском» (русском, японском, и т.д.)</p>
<p>В этом очерке мы будем использовать язык F# для написания DSLи которая помогает нам делать оценку трудоемкости проектов. Более заумная версия этой DSLины используется у нас на производстве. Сразу скажу, что тот код который я покажу далеко не идеальный пример использования F#, так что все «камни в огород» в плане стиля программирования буду игнорировать. Суть-то не в этом. Впрочем, если есть желание пооптимизировать &ndash; пожалуйста.</p>
<p>Ах да, и вот еще что &ndash; сразу дам ссылочки на <a href="http://www.codeproject.com/KB/dotnet/dslfsharp.aspx" title="http://www.codeproject.com/KB/dotnet/dslfsharp.aspx">оригинал статьи</a> и <a href="http://www.codeproject.com/KB/dotnet/dslfsharp/EstimationDSL.zip" title="http://www.codeproject.com/KB/dotnet/dslfsharp/EstimationDSL.zip">исходный код</a>. Код &ndash; это по сути дела один <code>.fs</code> файл. Надеюсь у вас получится его скомпилировать. Для того чтобы оценить то, как он работает, вам потребуется Project 2007. Если у вас его нет, спросите ближесидящего PMа.</p>
<p>Итак, в путь!</p>
<h3>Описание Проблемы</h3>
<p>Когда кому-то нужно заказное ПО, этот кто-то (обычно именуется «заказчик») шлет разным фирмам так называемый RFP (request for proposal), то есть по сути дела описание своего проекта. На этот запрос фирмы-разработчики делают проектный план (если инфы достаточно &ndash; если нет, то начинают общаться), пакуют его в красивый PDF и отсылают назад, причем естественно чем быстрее произведена оценка (эстимейт), чем она качественней и чем лучше преподнесена, тем больше вероятность что клиент будет с вами общаться. Получается что в интересах всей фирмы сделать этот эстимейт хорошо и быстро.</p>
<p>Кто-то должен делать этот эстимейт&hellip; обычно «крайним» является какой-нибудь релаксирующий под музыку PM, который достаточно знает технологический стек и имеет хоть чуть-чуть опыта чтобы прикинуть что и так (механизм peer review, если он налажен, все равно сгладит все его косяки). Так вот, наш РМ должен оценить этапы проекта и сделать красивый временной график (кажется это называется GANTT chart) чтобы наглядно показать на что пойдут усилия разработчиков, тестировщиков, и свои тоже. Тут возникает проблема.</p>
<p>Проблема в том что MS Project, та тулза которой создается это счастье, не очень то быстра на подъем когда нужно постоянно реструктурировать оценки, менять таски местами, корректировать ресурсы, оверхед, ну и т.д. Все становится слишком напряжно, особенно если вы придерживаетесь правила что «каждый клиент должен получить эстимейт в пределах одного дня своей временной зоны». Приходится изворачиваться, и наш DSL &ndash; это попытка упростить и ускорить оценочную деятельность для всех участников.</p>
<h3>Выбор Языка</h3>
<p>Проблему мы описали, теперь о решении. В принципе, для описания проекта можно сделать «свободную» DSL где можно использовать любой синтаксис и потом парсить его с помощью умных фреймворков, но это как-то скучно если учесть что эти фреймворки ничего не добавят в результат, зато наверняка принесут немного головной боли. Поэтому более простым подходом будет выбор языка (в нашем случае &ndash; языка в .Net стеке) который позволит писать на «почти Английском языке» и не будет сильно напрягать нетехнический персонал (хотя если РМ не умеет программировать, то это не к нам).</p>
<p>Из популярных языков для DSLей конечно нужно отметить Boo, который неплохо пропиарил Ayende в <a href="http://www.manning.com/rahien/" title="http://www.manning.com/rahien/">своей книге</a>. Boo &ndash; очень мощный язык, но в данном случае его метапрограммисткая мощь нам не потребуется. Еще есть язык Ruby который тоже популярен в плане DSLей но я к сожалению с ним не знаком (досадное упущение), поэтому не могу его порекоммендовать. Ну и последний выбор, на котором я и остановился, это F#.</p>
<p>Почему F# хорош для DSLей? Потому что его синтаксис не нагружает разум. Можно писать почти на чистом английском. DSL читаем кем угодно. Единственная проблема &ndash; это то, что F# ориентирован на неизменчивость переменных (immutability), поэтому в нашем контексте некоторые его конструкты будут выглядеть немного неестественно. Но, как я уже сказал, суть не в этом &ndash; ведь DSL это всего лишь трансформатор, «кондуит сознания».</p>
<h3>Первый Стейтмент</h3>
<p>Начнем с простого. Вот как выглядит первая строчка в описании проекта:</p>
<p><pre class="brush: fsharp;">
project &quot;Write F# DSL Article&quot; starts_on &quot;16/8/2009&quot;
</pre>
<p>То что вы видите выше &ndash; совершенно легальное выражение в F#. Мы просто вызываем метод <code>project</code> и передаем ему три параметра &ndash; имя проекта, некой токен (пустышку, которая служит англосинтактическим сахаром), и время начала проекта. Фактически мы делаем примерно то же, что делают с тестами в BDD &ndash; а именно, пытаются сделать их читабельными для нетехнарей.</p>
<p>DSLина которую мы пишем сама по себе основана на ООР. Наша цель &ndash; через DSL поддержать все те конструкции, к которым привыкли РМы. Одна из этих конструкций &ndash; проект, поэтому с него пожалуй и начнем:</p>
<p><pre class="brush: fsharp;">
type Project() =
  [&lt;DefaultValue&gt;] val mutable Name : string
  [&lt;DefaultValue&gt;] val mutable Resources : Resource list
  [&lt;DefaultValue&gt;] val mutable StartDate : DateTime
  [&lt;DefaultValue&gt;] val mutable Groups : Group list
</pre>
<p>Вот, я же предупреждал что F# не будет смотреться слишком шикарно если писать с поддержкой mutability. Странные конструкции выше &ndash; это публичные поля, которые можно изменять. В плане коллекций я воспользовался F#овским <code>list</code> вместо <code>List&lt;T&gt;</code> из <code>System.Collections.Generic</code>. Разницы особой нет.</p>
<p>В отличии от C#, в F# у нас есть нечто, что на первый взгляд можно именовать «global scope», то есть декларировать переменные и функции можно как бы «на верхнем уровне», без всяких явно описанных классов, модулей и пространств имен. Давайте этим незамедлительно воспользуемся:</p>
<p><pre class="brush: fsharp;">
/* Делаем проект. Переменная my_project будет в последствии переопределена. */
let mutable my_project = new Project()
</pre>
<p>Мы только что создали «переменную дефолтного проекта». Естественно что терминология в F# немного другая, но не суть. Имя мы выбрали такое, чтобы в конце можно было пафосно написать <code>prepare my_project</code> и запустить автогенерацию проектного плана. А пока давайте посмотрим на функцию <code>project</code>, с которой все и начинается.</p>
<p><pre class="brush: fsharp;">
/* Поддержка создания нового проекта.
Как и обещалось, переменная my_project переписывается.
Полезно если нужно делать несколько проектов подряд. */
let project name startskey start =
  my_project &lt;- new Project()
  my_project.Name &lt;- name
  my_project.Resources &lt;- []
  my_project.Groups &lt;- []
  my_project.StartDate &lt;- DateTime.Parse(start)
</pre>
<p>Ну вот. В принципе, на этом этапе можно смело бросать читать статью и идти экспериментировать &ndash; ведь всю суть создания DSLей на F# мы только что показали. Дальше будет разбор семантики и собственно демонстрация того, как разруливаются разные тонкости.</p>
<h3>Работа со Списками</h3>
<p>Работа в проекте выполняется ресурсами, то есть людьми. Вы ресурс, и я ресурс &ndash; не очень-то приятно, не так ли? Тем не менее, у каждого ресурса есть некий титул (к пр. «Junior Developer»), имя («John») а также рейт &ndash; сколько долларов в час фирма хочет получать в месяц за работу этого ресурса. Давайте сначала посмотрим на определение этого самого ресурса:</p>
<p><pre class="brush: fsharp;">
type Resource() =
  [&lt;DefaultValue&gt;] val mutable Name : string
  [&lt;DefaultValue&gt;] val mutable Position : string
  [&lt;DefaultValue&gt;] val mutable Rate : int
</pre>
<p>Теперь можно посмотреть на то, как будет выглядеть создание ресурса в нашей DSL:</p>
<p><pre class="brush: fsharp;">
resource &quot;John&quot; isa &quot;Junior Developer&quot; with_rate 55
</pre>
<p>Конечно же, для поддержки выражения выше мы используем то же шаманство что и для проектов, а именно:</p>
<p><pre class="brush: fsharp;">
let resource name isakey position ratekey rate =
  let r = new Resource()
  r.Name &lt;- name
  r.Position &lt;- position
  r.Rate &lt;- rate
  my_project.Resources &lt;- r :: my_project.Resources
</pre>
<p>Как вы уже наверное догадались, мы создаем ресурс и добавляем его в <em><font face="Trebuchet MS" />начало</font></em> списка. Это значит что когда придет время «выстраивать» ресурсы и прочие элементы которые хранятся в списках, каждый список придется разворачивать задом на перед. Для меня это не проблема, но если вам не нравится &ndash; используйте <code>List&lt;T&gt;</code>.</p>
<h3>Строковые Ссылки</h3>
<p>Следующая концепция в нашей DSL &ndash; это группы заданий. Группу заданий в проекте обычно выполняет один человек, что способствует поддержанию «когнитивного фокуса». Группу мы определяем вот так:</p>
<p><pre class="brush: fsharp;">
group &quot;Project Coordination&quot; done_by &quot;Dmitri&quot;
</pre>
<p>А вот как выглядит объект, который содержит данные о группе:</p>
<p><pre class="brush: fsharp;">
type Group() =
  [&lt;DefaultValue&gt;] val mutable Name : string
  [&lt;DefaultValue&gt;] val mutable Person : Resource
  [&lt;DefaultValue&gt;] val mutable Tasks : Task list
</pre>
<p>Видите &ndash; группы ссылается на <em><font face="Trebuchet MS" />объект</font></em> типа <code>Resource</code>, а мы передаем имя (строку). Но это не проблема, так как поиск в списках никто не отменял:</p>
<p><pre class="brush: fsharp;">
let group name donebytoken resource =
  let g = new Group()
  g.Name &lt;- name
  g.Person &lt;- my_project.Resources |&gt; List.find(fun f -&gt; f.Name = resource)
  /* Ищем тот ресурс что нам нужен. */
  my_project.Groups &lt;- g :: my_project.Groups
</pre>
<p>В отличии от LINQ, не надо вызывать <code>Single()</code> чтобы получить результат поиска.</p>
<h3>Побольше Гибкости</h3>
<p>Группы тасков (заданий) состоят из, эммм, заданий. А задание неплохо определять вот так:</p>
<p><pre class="brush: fsharp;">
task &quot;PayPal Integration&quot; takes 2 weeks
</pre>
<p>Это тоже реально в F#! Для начала, мы делаем так, чтобы те токены которые мы обычно используем для «сахара» содержали внятные значения:</p>
<p><pre class="brush: fsharp;">
let hours = 1
let hour = 1
let days = 2
let day = 2
let weeks = 3
let week = 3
let months = 4
let month = 4
</pre>
<p>Теперь мы можем определить наш <code>Task</code>:</p>
<p><pre class="brush: fsharp;">
type Task() =
  [&lt;DefaultValue&gt;] val mutable Name : string
  [&lt;DefaultValue&gt;] val mutable Duration : string
</pre>
<p>А добавление таска в группу выглядит вот так:</p>
<p><pre class="brush: fsharp;">
let task name takestoken count timeunit =
  let t = new Task()
  t.Name &lt;- name
  let dummy = 1 + count
  /* Это хак. Суть в том, чтобы заставить компилятор думать что count - это int.
     Иначе банально не сработает угадываение типов, ведь в качестве подсказок есть 
     только String.Format ниже. */
  match timeunit with
  | 1 -&gt; t.Duration &lt;- String.Format(&quot;{0}h&quot;, count)
  | 2 -&gt; t.Duration &lt;- String.Format(&quot;{0}d&quot;, count)
  | 3 -&gt; t.Duration &lt;- String.Format(&quot;{0}wk&quot;, count)
  | 4 -&gt; t.Duration &lt;- String.Format(&quot;{0}mon&quot;, count)
  | _ -&gt; raise(ArgumentException(&quot;only spans of hour(s), day(s), week(s) and month(s) are supported&quot;))
  /* А вот тут мы сохраняем продолжительность таска как строковый литерал,
который угоден Project'у. */
  let g = List.hd my_project.Groups
  g.Tasks &lt;- t :: g.Tasks
</pre>
<p>В коде выше мы в зависимости от временной константы подстраиваем продолжительность таска. Для того чтобы найти ту группу, в которую нужно добавить таск, мы используем <code>List.hd</code> &ndash; ведь группы тоже задом наперед.</p>
<h3>Автогенерация Проекта</h3>
<p>Ну вот и все! Теперь мы можем вызвать одну помпезную комманду чтобы сгенерировать наш проектный план:</p>
<p><pre class="brush: fsharp;">
prepare my_project
</pre>
<p>Далее идет самый сложный кусочек &ndash; использование Office Automation и F# в тандеме для генерации плана из нашей DSLки. Я постарался прокомментировать код чтобы было понятно что к чему.</p>
<p><pre class="brush: fsharp;">
let prepare (proj:Project) =
  let app = new ApplicationClass()
  app.Visible &lt;- true
  let p = app.Projects.Add()
  p.Name &lt;- proj.Name
  /* Добавляем в проект ресурсы.
     Стоимость ресурса в овертайм считаем как полтора рейта. */
  proj.Resources |&gt; List.iter(fun r -&gt;
    let r2 = p.Resources.Add()
    r2.Name &lt;- r.Position // position, not name :)
    let tables = r2.CostRateTables
    let table = tables.[1]
    table.PayRates.[1].StandardRate &lt;- r.Rate
    table.PayRates.[1].OvertimeRate &lt;- (r.Rate + (r.Rate &gt;&gt;&gt; 1)))
  /* Делаем корневой таск с именем проекта. */
  let root = p.Tasks.Add()
  root.Name &lt;- proj.Name
  /* Добавляем группы. */
  proj.Groups |&gt; List.rev |&gt; List.iter(fun g -&gt; 
    let t = p.Tasks.Add()
    t.Name &lt;- g.Name
    t.OutlineLevel &lt;- 2s
    /* Кто отвечает за группу? */
    t.ResourceNames &lt;- g.Person.Position
    /* Добавляем задания. */
    let tasksInOrder = g.Tasks |&gt; List.rev
    tasksInOrder |&gt; List.iter(fun t2 -&gt;
        let t3 = p.Tasks.Add(t2.Name)
        t3.Duration &lt;- t2.Duration
        t3.OutlineLevel &lt;- 3s
        /* Ищем предыдущий таск. Если такой имеется, делаем связку
           между этим таском и предыдушим. Предполагается что все такси следуют один за другим. */
        let idx = tasksInOrder |&gt; List.findIndex(fun f -&gt; f.Equals(t2))
        if (idx &gt; 0) then 
          t3.Predecessors &lt;- Convert.ToString(t3.Index - 1)
      )
    )
</pre>
<p>Ну вот мы и «развернули» списки с помощью <code>List.rev</code> &ndash; не самая быстрая операция, конечно, но это не важно. Главное, что скрипт работает и генерит проекты &ndash; определяет ресурсы, группы тасков и сами таски. А что еще РМу надо? (На самом деле много чего :)</p>
<p>А вот как может выглядеть полное описание проекта с использованием нашей DSL:</p>
<p><pre class="brush: fsharp;">
project &quot;F# DSL Article&quot; starts &quot;01/01/2009&quot;
resource &quot;Dmitri&quot; isa &quot;Writer&quot; with_rate 140
resource &quot;Computer&quot; isa &quot;Dumb Machine&quot; with_rate 0
group &quot;DSL Popularization&quot; done_by &quot;Dmitri&quot;
task &quot;Create basic estimation DSL&quot; takes 1 day
task &quot;Write article&quot; takes 1 day
task &quot;Post article and wait for comments&quot; takes 1 week
group &quot;Infrastructure Support&quot; done_by &quot;Computer&quot;
task &quot;Provide VS2010 and MS Project&quot; takes 1 day
task &quot;Download and deploy TypograFix&quot; takes 1 day
task &quot;Sit idly while owner waits for comments&quot; takes 1 week
prepare my_project
</pre><br />
<h3>Заключение</h3>
<p>Надеюсь этот очерк показал вам, что делать DSL в F# &ndash; это просто. Конечно, тот пример что я привел выше упрощен по сравнению с тем что мы реально испольуем. Но это, как говорится, секреты фирмы. До новых встреч!</p>
<br />Рубрика:.NET, dsl, f#  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/nesteruk.wordpress.com/614/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/nesteruk.wordpress.com/614/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/nesteruk.wordpress.com/614/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/nesteruk.wordpress.com/614/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/nesteruk.wordpress.com/614/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/nesteruk.wordpress.com/614/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/nesteruk.wordpress.com/614/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/nesteruk.wordpress.com/614/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/nesteruk.wordpress.com/614/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/nesteruk.wordpress.com/614/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/nesteruk.wordpress.com/614/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/nesteruk.wordpress.com/614/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/nesteruk.wordpress.com/614/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/nesteruk.wordpress.com/614/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=nesteruk.wordpress.com&#038;blog=373435&#038;post=614&#038;subd=nesteruk&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://nesteruk.wordpress.com/2009/08/29/dsl-fsharp/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f01cbd2238e2a78e4c43fa596f51d6a1?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&#38;r=G" medium="image">
			<media:title type="html">nesteruk</media:title>
		</media:content>
	</item>
	</channel>
</rss>
