<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6031647961506005424</id><updated>2012-01-27T13:52:55.703+02:00</updated><category term='ua'/><category term='ru'/><category term='lisp'/><category term='etc'/><category term='universe'/><category term='cl'/><category term='en'/><title type='text'>Lisp, the Universe and Everything</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>54</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-3733887250519737951</id><published>2011-12-21T09:47:00.004+02:00</published><updated>2011-12-21T11:20:53.066+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Реальная норма часов работы программиста в день</title><content type='html'>В 2009-10 годах у меня был некоторый опыт удаленной работы программистом на основе почасовой оплаты. С тех пор я периодически задумывался над тем, сколько же &lt;b&gt;реальных часов работы над кодом в день&lt;/b&gt; должен в среднем выдавать программист. На самом деле, этот вопрос, а точнее его последствия, актуален для любого формата работы: как офисной, так и удаленной, так и всего, что между. В "офисном" варианте оплачивается фиксированное время работы и задача в оптимизации реального времени в рамках этого интервала (гибкий график, уменьшение количества встреч и т.п.) Проблемой фриланс-варианта является то, что большинство заказчиков не понимают, что они должны платить не только за время реальной работы над кодом, но и за сопутствующие активности, типа ковыряния в носу, и/или не знают, какое должно быть соотношение этих активностей к собственно программированию.&lt;br /&gt;&lt;br /&gt;И вот, на днях на хакерньюз я наткнулся на отличную статью профессиональной писательницы &lt;a href="http://thisblogisaploy.blogspot.com/2011/06/how-i-went-from-writing-2000-words-day.html"&gt;о повышении эффективности ее труда в 5 раз&lt;/a&gt; (с 2 до 10 тысяч слов в день). Фактически, ей удалось это сделать за счет выработки метода систематического вхождения в поток. А как все мы творческие работники знаем, поток — это залог эффективной работы*. Но, что в этой статье для меня было действительно важным — это то, что человек, добившийся такой эффективности, констатировал факт: собственно непрерывной работы у нее в среднем 4 часа в день — в самых удачных случаях до 7 — плюс 2 часа с утра, которые расходуются на подготовительную работу. А если вырабатывать больше, то эффективность начинает падать. Не говоря о том, что можно перегореть.  &lt;br /&gt;&lt;br /&gt;Это наблюдение совпадает и с моим опытом: у меня такое же количество (4-6) часов эффективного писания кода в день. В этом нет ничего удивительного, поскольку труд программиста ничем не выделяется из ряда других творческих профессий: писателя, дизайнера или же композитора. Что же тогда делать с 8-ми часовым рабочим днем? На самом деле, ничего плохого в том, что только 50-75% рабочего времени тратится собственно на работу нет: остальное время тоже не пропадает зря, посколько тратится на комуникацию (без которой любая компания и проект обречены), а также какие-то нерабочие активности, которые формируют ту самую мифическую "корпоративную культуру". И даже если этих активностей нет (фриланс) — это не значит, что: а). программист может это время потратить на работу над кодом б). программисту они не нужны (нужны, возможно, только в другой форме: социализация нужна всем).&lt;br /&gt;&lt;br /&gt;Т.е. выходит 2 числа, характеризующие нормального программиста: максимум 6 часов (а в среднем: 4 часа) программирования в день и коэфициент 1.5 соотношения часов программирования к часам работы. И эти числа нужно принять как исходные данные, на основании которых менеджеры могут стоить какие-то предположения, оценки и методики.&lt;br /&gt;&lt;br /&gt;Кроме того, такой взгляд разрешает для меня &lt;b&gt;диллему эстимейтов&lt;/b&gt;: должен и может ли, вообще, их делать программист?&lt;br /&gt;&lt;br /&gt;Функция time профилировщиков программ выдает обычно 2 числа: total time и real/cpu time. Первое значение — это фактическое время, которое прошло от старта программы до завершения. На него могут влиять такие факторы, как кеширование, ожидание ввода-вывода и т.п. А сpu time — это время, которое программа действительно выполнялась на процессоре. По-хорошему, именно это, второе время, может научиться оценивать программист: т.е. время, которое ему понадобиться для написания и отладки кода, когда он будет находится в "разогретом" режиме работы. А вот общее время — которое, на самом деле, интересует бизнес — может научиться оценивать только менеджер, беря во внимание оценки программистов (и их историческое качество), а также 100500 других факторов, которые могут повлиять на режим его работы: кеширование, своевременное заполнение пайплайнов, переключене контекста и т.д.&lt;br /&gt;&lt;br /&gt;* Те, кто не знает, может очень быстро узнать из уст человека, который сформулировал эту концепцию (или из его классической книги Flow):&lt;br /&gt;&lt;object width="526" height="374"&gt;&lt;param name="movie" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;param name="bgColor" value="#ffffff"&gt;&lt;/param&gt;&lt;param name="flashvars" value="vu=http://video.ted.com/talk/stream/2004/Blank/MihalyCsikszentmihalyi_2004-320k.mp4&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/MihalyCsikszentmihalyi-2004.embed_thumbnail.jpg&amp;vw=512&amp;vh=288&amp;ap=0&amp;ti=366&amp;lang=&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=mihaly_csikszentmihalyi_on_flow;year=2004;theme=unconventional_explanations;theme=the_creative_spark;theme=how_the_mind_works;event=TED2004;tag=Culture;tag=Global+Issues;tag=happiness;tag=music;tag=psychology;tag=work;&amp;preAdTag=tconf.ted/embed;tile=1;sz=512x288;" /&gt;&lt;embed src="http://video.ted.com/assets/player/swf/EmbedPlayer.swf" pluginspace="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" wmode="transparent" bgColor="#ffffff" width="526" height="374" allowFullScreen="true" allowScriptAccess="always" flashvars="vu=http://video.ted.com/talk/stream/2004/Blank/MihalyCsikszentmihalyi_2004-320k.mp4&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/MihalyCsikszentmihalyi-2004.embed_thumbnail.jpg&amp;vw=512&amp;vh=288&amp;ap=0&amp;ti=366&amp;lang=&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=mihaly_csikszentmihalyi_on_flow;year=2004;theme=unconventional_explanations;theme=the_creative_spark;theme=how_the_mind_works;event=TED2004;tag=Culture;tag=Global+Issues;tag=happiness;tag=music;tag=psychology;tag=work;&amp;preAdTag=tconf.ted/embed;tile=1;sz=512x288;"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-3733887250519737951?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/3733887250519737951/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=3733887250519737951' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3733887250519737951'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3733887250519737951'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2011/12/blog-post.html' title='Реальная норма часов работы программиста в день'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-3175749400227790189</id><published>2011-11-30T20:18:00.009+02:00</published><updated>2011-12-06T19:01:21.295+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>Clojure &amp; Complexity</title><content type='html'>I gave a rather messy lightning talk at the recent ECLM on this topic (see below). I think, the messiness can be attributed mostly to my undesire to criticize anything, built with good intentions, including Clojure. Yet in software development there's clearly a need for thorough evaluation of different approaches, languages and technologies, because, we must admit, lots and lots of decisions on such things as architecture or language/platform choice are made on purely subjective and even emotional bases (see "hype"). So below is a more detailed account of the (excessive) complexities, I've encountered working with Clojure in a real-world environment: a half-year project to develop a part of a rather big Java-based server-side system. Also I should note, that I was &lt;a href="http://lisp-univ-etc.blogspot.com/2008/10/clojure-for-lisp-programmers-talk.html"&gt;following&lt;/a&gt; Clojure almost since its initial public release, have participated in the early flames on c.l.l. and even edited a Clojure introduction article in the Russian functional programming journal fprog.ru. But this was only the first chance to make a reality check...&lt;br /&gt;&lt;br /&gt;But, first of all, I'd like to refer you to the talk of Rich Hickey at the Strange Loop conference "&lt;a href="http://www.infoq.com/presentations/Simple-Made-Easy"&gt;Simple made Easy&lt;/a&gt;", the principles of which really resonate with me. Yet it's so often the case, that it's hard to follow your abstract principles, when you're faced with reality (also guilty of that). And another point is that it's not really beneficial to push the principles to the extreme, because there's always the other side, and engineering is the art of making trade-offs: if you don't find room for them, the other side will bite you. So the points below basically boil down to these 2 things: examples of "complecting", and "things should be made as simple, as possible, but not simpler" (attributed to Einstein).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Interactive development&lt;/h3&gt;&lt;br /&gt;Lisp is considered a so called platform-language in a sense that it implies the existence of certain facilities and runtime environment constraints, that form a distinct and whole environment. Unlike some other languages, usually called scripting, which rely on a pre-existing environment, like the OS (e.g. Posix), web server, web browser etc. Other platform languages are, for example, Java, C# or Erlang, while scripting languages are JavaScript, Perl or PHP. Clojure is a language on a pre-existing platform, which is JVM, and so doesn't define its own platform. This is the source of, probably, the biggest &lt;b&gt;complecting&lt;/b&gt; in the language, as it tries to be a stand-alone dynamic, functional language, explicitly discouraging imperative object-oriented style. But the JVM-platform is oriented at static imperative object-oriented ones.&lt;br /&gt;&lt;br /&gt;From the Lisp dynamic point-of-view a lot of JVM's facilities are inferior:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;mostly static runtime image and object system with only partial opportunities for redefining things on-the-fly instead of a fully dynamic one&lt;br /&gt;&lt;/li&gt;&lt;li&gt;static namespaces (tied to a file system) instead of dynamic packages&lt;br /&gt;&lt;/li&gt;&lt;li&gt;static exception system instead of a dynamic (restartable) condition system&lt;br /&gt;&lt;/li&gt;&lt;li&gt;limited and flawed number representation system instead of a full numeric tower&lt;br /&gt;&lt;/li&gt;&lt;li&gt;more limited calling convention (only positional parameters and absense of multiple return values)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;more limited naming scheme&lt;br /&gt;&lt;/li&gt;&lt;li&gt;XML-based build facilities instead of eDSL-based ones - althouh, here Clojure can provide its own option, but the currently existing one - Leiningen is a weird beast of its own (for example, you can get &lt;code&gt;CLASSPATH&lt;/code&gt; out of it, but can't put it in except through &lt;code&gt;project.clj&lt;/code&gt;, which has a lot of limitations)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;absense of tail call optimization&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Surely, there are also some current advantages of the Java platform:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;availability of a concurrent GC (although it's not the default one)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;good JIT-optimizing compiler&lt;br /&gt;&lt;/li&gt;&lt;li&gt;and what's most important, larger amount of available tools and libraries&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Yet, if we return to the top of the list of shortfalls, we can see, why interactive development in Clojure is much less productive, then in Lisp. What adds to it is, that Clojure uses a one-pass compiler (not very modern).&lt;br /&gt;&lt;br /&gt;Going into more details on this will be a whole separate post, so I'll just sum up, that interactive development in Clojure is hampered both by the JVM sticking in different places (especially, if you work on projects, combining Clojure and Java code) and Clojure's own misfeatures.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Syntax&lt;/h3&gt;&lt;br /&gt;From its early days Clojure was positioned as a "modern" Lisp. And what this "modernity" actually implied is:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;more accessible syntax and broader support for vectors and maps as opposed to Common Lisp, in which only lists, allegedly, were first-class citizens&lt;br /&gt;&lt;/li&gt;&lt;li&gt;built-in concurrency primitives&lt;br /&gt;&lt;/li&gt;&lt;li&gt;being Lisp-1 instead of Lisp-2, which makes heavy functional style programming more concise&lt;br /&gt;&lt;/li&gt;&lt;li&gt;cleaning up some minor "annoyances": 4 equality operators, interning reader, etc.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Starting from the bottom, the minor issues make it not &lt;b&gt;easy&lt;/b&gt; to start, but they are actually conceptually &lt;b&gt;simple&lt;/b&gt; and useful, once you get accustomed. Lisp-1 vs Lisp-2 is a matter of taste: for example, I prefer &lt;code&gt;#'&lt;/code&gt;, because it's an annotation, while others perceive it as noise. And there's no objective advantage of one over another: yes, Lisp-1 makes something like &lt;code&gt;(&amp;lt;map&amp;gt; &amp;lt;/key&amp;gt;)&lt;/code&gt; instead of &lt;code&gt;(gethash &amp;lt;map&amp;gt; &amp;lt;key&amp;gt;)&lt;/code&gt; possible, yet it makes macros more complicated. And concurrency I'll discuss separately.&lt;br /&gt;&lt;br /&gt;What's left is broader support for vectors and maps, including destructuring. I agree, that declarative syntax for common datastructures is a crucial for productive use of any language up to the point of defining the same literal syntax (&lt;code&gt;{}&lt;/code&gt;) for hash-tables in CL. Thankfully, that is supported by the language, so this syntax is as first-class in CL, as in Clojure, and, as in many aspects, nothing prevents "modernizing" Lisp in this aspect without creating a whole separate language... This doesn't hold for Clojure, as it doesn't have facilities to control the reader in the same way CL does: actually, in this regard Clojure is very different from Lisp — it hardly provides facilities for controlling any aspect of the language — and this control is a critical part of Lisp's DNA.&lt;br /&gt;&lt;br /&gt;And pushing syntax to the extreme has it's shortcomings in Clojure as well. Rich argues, that defining both lists and vectors with parens (in Lisp list is &lt;code&gt;'()&lt;/code&gt; and vector is &lt;code&gt;#()&lt;/code&gt;) is &lt;b&gt;complecting&lt;/b&gt;. But, I'd say, that a much harder case of &lt;b&gt;complecting&lt;/b&gt; is this:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;)))))))])))))])])&lt;/code&gt; — the joy of debugging ASTs&lt;/blockquote&gt;&lt;br /&gt;And it's &lt;a href="http://twitter.com/mitsuhiko/status/88527153158815744"&gt;not even about Clojure&lt;/a&gt;: although here it's even worse, because for some reason, that escapes me, &lt;code&gt;let&lt;/code&gt; (and &lt;code&gt;defn&lt;/code&gt;, and many others) uses vectors for argument lists. Aren't they called &lt;b&gt;lists&lt;/b&gt; for a reason? So this once again actualizes the problem of counting closing parens, effectively solved for Lisp long ago with emacs.&lt;br /&gt;&lt;br /&gt;At the same time "modern" Clojure poorly supports such things as keyword arguments in functions or multiple return values and many other not so "modern", but very effective facilities, that I personally would expect to see in a modern language...&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;There's only one true way: functional&lt;/h3&gt;&lt;br /&gt;In my talk I referred to one of the ugliest pieces of code, I've ever written, which was a very complicated Clojrue loop, i.e. a &lt;code&gt;loop/recur&lt;/code&gt; thing (and I've honestly tried to factor it out).&lt;br /&gt;&lt;br /&gt;Basically there are two opposite approaches to iteration: imperative looping and functional recurring. Many languages have a strong bias towards one or another, like Python discouraging recursion and Clojure discouraging imperative loops by imposing immutability. But the thing is, that there are problems, for which one of the approaches yields by far more concise and understandable code. If you want to traverse a tree, recursion is a way to go. While if you are accumulating several sequences at once, which may reference results, obtained at the previous computations and also at each iteration there's not one, but several outcomes, recursions often becomes too messy. Yet in Clojure there's not even good support for recursion (which has an advantage of factoring different pieces of code into functions), but a special construct &lt;code&gt;loop/recur&lt;/code&gt;, which shares the downsides of both approaches and does hardly provide any of the benefits. That's a pity, as iteration is the basic programming construct and no code file can do without it. And here we see a case of detrimental over-simplification.&lt;br /&gt;&lt;br /&gt;And there are also lazy sequences, which &lt;b&gt;complect&lt;/b&gt; sequences and streams. In theory, those are the same things, but, as the saying goes, in theory, theory and practice are the same, but in practice... Surely this makes writing a compiler &lt;b&gt;easier&lt;/b&gt; at the cost of complicating reasoning about sequences in day-to-day programming.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;EDIT:&lt;/b&gt; as was correctly pointed by the commentators, you can have sort of mutable local variables with transients, and that allows to express some of the imperative loops in a more concise manner.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Concurrency&lt;/h3&gt;&lt;br /&gt;In the early days of Clojure in one of the discussions on c.l.l. S.Madhu (if I remember correctly) called Clojure's concurrency primitives "snake oil". I thought, that there may be some personal issues in such attitude, but having tried it for myself and learned about all the alternatives in the concurrency space, I don't think it was too far from reality. First of all, Clojure addresses only shared-state concurrency on a single computer, while most hard concurrency problems appear in the context of distributed systems and are currently solved with approaches, like the Actor model or MapReduce. And on the single machine I've seen very few problems, that can't be solved with a combination of thread pools (with thread-local storage), Lisp-style special variables and databases. In fact Clojure provides its own (often limited) variants of all the above mentioned approaches and nothing more:&lt;br /&gt;- agents instead of actors&lt;br /&gt;- vars (analogue of CL's special variables)&lt;br /&gt;- STM for databases&lt;br /&gt;- map/reduce, pmap&lt;br /&gt;&lt;br /&gt;To be able to provide them Clojure imposes some restrictions, most notably the one of immutability. Also in their implementation it doesn't follow the Lisp principle of giving control to the programmer: you can't swap one STM variant/strategy for the other. Heck, you can't even control the number of threads, that &lt;code&gt;pmap&lt;/code&gt; uses!&lt;br /&gt;&lt;br /&gt;Among all this approaches, STM is the most notable one (as others are just a copy of the established technologies). And it has a tough competition from databases and other transactional datastores. The advantages are no impedance mismatch and, possibly, better efficiency. Yet the database is language-agnostic, which is often useful, it's more accessible and understandable. And, what's most important: there's durability and there's a choice, that you can utilize, depending on your needs. The best use case for STM I've found so far was to hold statistical counters, accessed simultaneously from many threads, yet this problem is easily solvable with Redis, for example. And the same applies to other uses I can think of, So, the paradox of Clojure is that, although it was ushered with the idea of solving concurrency problems, it still has a lot to prove in this space: and not with &lt;a href="http://blip.tv/clojure/clojure-concurrency-819147"&gt;toy examples of ant colonies&lt;/a&gt;, but real-world applications, made possible with its approach (like RabbitMQ or Ejabberd showcasing Erlang's aptitude for building massively parallel systems).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Final thoughts&lt;/h3&gt;&lt;br /&gt;I've written a long article, but there are many more details left out (just adding code snippets, will make it twice as big). There's also a lot of good things about Clojure, which I didn't mention: its seamless integration with Java, for example, which makes it a good scripting language for Java applications. Macros, basic datastructures, vars, they all work great. Ring was also an enlightenment.&lt;br /&gt;&lt;br /&gt;Yet, overall the language doesn't leave up to its promise of being a modern Lisp: actually, it's not a Lisp at all. Well, how do you define Lisp? S-expressions, macros, closures etc? Surely, all (at least most of) those features may be present, although Dylan was a Lisp without s-expressions, for example. But, in my opinion as a Lisp programmer, what makes a true Lisp is dynamicity, simplicity and putting control in developer's hands (flexibility and extensibility). The mentioned features are mostly a derivative of this principles, and also they all should have a backend for extension: reader, compiler, condition system, object system, special variables etc, they all do in Lisp. And Clojure gives up on all these principles if not altogether, than substantially: many of the features are there and the new ones arrived, but the backend is missing.&lt;br /&gt;&lt;br /&gt;Common Lisp is well optimized for the common use-cases in the design space of dynamic flexible languages. And it's probably very close to local maximum in it. At least it's good enough. Clojure is more of a hybrid, and hybrids are built by &lt;b&gt;complecting&lt;/b&gt;: in Clojure's case complecting Lisp and Java, Lisp and Haskell. The result is interesting, but, in my opinion, it's not optimal. Surely it has its use-cases.&lt;br /&gt;&lt;br /&gt;Still, Clojure sees decent adoption (which is good, because it proves, that parens aren't a problem after all :) I think it's a combination of several things. The language still has a lot of good stuff from Lisp (as well as some good things from Haskell, although they don't match very well), and those who start using it are mostly Java and Ruby programmers, for whom it breaks the psychological barriers to using Lisp (we all know the FUD). The other thing is lack of a decent native (not like Jython or JRuby) dynamic language on the JVM. And finally, there's marketing, i.e. Clojure concurrency primitives. And the challenge for Clojure community is to produce a "killer" application, that will utilize them, or the hype will wane, and pretty soon...&lt;br /&gt;&lt;br /&gt;&lt;iframe src="http://blip.tv/play/AYLgoXEC.html" width="550" height="442" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;embed type="application/x-shockwave-flash" src="http://a.blip.tv/api.swf#AYLgoXEC" style="display:none"&gt;&lt;/embed&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-3175749400227790189?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/3175749400227790189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=3175749400227790189' title='63 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3175749400227790189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3175749400227790189'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2011/11/clojure-complexity.html' title='Clojure &amp; Complexity'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>63</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-7327561326956732500</id><published>2011-11-27T19:39:00.002+02:00</published><updated>2011-11-27T19:40:50.182+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>Videos from ECLM 2011</title><content type='html'>Today I've finally finished uploading all the videos to the ECLM channel on &lt;a href="http://blip.tv/eclm"&gt;blip.tv/eclm&lt;/a&gt;. There are a lot of very interesting talks and the topics range from Lisp-based companies' experiences to exciting Lisp projects (like quicklisp or zen X server) to community issues (like the announcement of the Stichting Common Lisp Foundation or good Lisp style). Of 7 lengthy talks, announced at &lt;a href="http://weitz.de/eclm2011"&gt;the event's website&lt;/a&gt;, only 5 are available now:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Luke Gorrie's account of his exciting startup Teclo Networks will be published somewhat later not to compromise the business (and it's a great talk, so don't miss it :),&lt;/li&gt;&lt;li&gt;and Paul Miller's showcase of LinkExplorer, a Windows LispWorks CAPI-based GUI application was not recorded by the request of his company.&lt;/li&gt;&lt;/ul&gt;In total, there are 12 videos, including the lightning talks (and the lucky 13th will join later). My personal favourite is Jack Harper's story of &lt;a href="http://blip.tv/episode/5741240"&gt;building Secure Outcomes&lt;/a&gt;, with a lot of invaluable engineering insights.&lt;br /&gt;&lt;br /&gt;The sound quality is not stellar, because, unfortunetely, there was a laying of the ground 50Hz signal on the mic's signal at the site. But judging from my experiences in organizing TEDxKyiv, sound is the usual point of failure at conferences, and it's pretty hard to get it right. Yet, I hope, that next meeting will have a more professional recording set up (maybe through CLF), because, in my opinion, these videos are extremely valuable to the community. I really miss some talks from ECLM 2009, like Kuroda Hisao's, Dave McClure's or Dan Weinreib's, and most of all, the Piano.aero one...&lt;br /&gt;&lt;br /&gt;For me the overall meeting experience was really fantastic, and the talks actually form only a minor half of that. Immersing myself into the Lisp community and participating in so many profound conversations with &lt;a href="http://twitter.com/#!/vseloved/status/128583480320864256"&gt;brilliant Lisp programmers&lt;/a&gt; was the really exciting part. The good overview of the conference experience is given by &lt;a href="http://kvardek-du.kerno.org/2011/10/eclm-2011.html"&gt;Luís Oliveira&lt;/a&gt;. So I'm really looking forward for the ECLM 2012. Maybe, organize it in a different European city this time?..&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-7327561326956732500?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/7327561326956732500/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=7327561326956732500' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/7327561326956732500'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/7327561326956732500'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2011/11/videos-from-eclm-2011.html' title='Videos from ECLM 2011'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-4592857771883152198</id><published>2011-04-05T14:45:00.003+03:00</published><updated>2011-04-05T14:48:12.754+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>Book review: Algorithms of the Intelligent Web</title><content type='html'>&lt;img src="http://www.manning.com/marmanis/marmanis_cover150.jpg" style="float:right;margin-left:20px;" height="140" /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;blockquote&gt;TL;DR The book should have been named "Building Machine Learning Apps in Java for Dummies". Worth reading if such name excites you.&lt;br /&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;This book aims to become a comprehensive guide in commonly used Machine Learning technics. And generally it succeeds in that with one reservation: it more resembles a sophomore synopsis, than a professor's lecture.&lt;br /&gt;&lt;br /&gt;I should say, that I couldn't manage to finish the book, and had read only Chapters 1,2 and 5. But the structure of each chapter is roughly the same: a brief high-level description of the common approaches in the described sphere. Then a list of Java libraries, available for the task. Selection of one of them and a description of how solid, robust and mature it is. Then several pages of Java code, showing how to prepare input parameters and actually make use of the libraries. Finally a note, that there are some limitations or pecularities, but discussing them in more detail is out of the scope of the book. Thus, the amount of new material for anyone at least slightly familiar with the subject and programming in general is minimal (I would say, that for me it was around 10%.) No insights, nothing novel, that can't be found on the first page of Google results on the topic, no account of personal experience or "war stories". Such book doesn't require 20 years of industrial experience under the hood to write — this could be done by any working programmer. The recipe seems simple: for each topic take the first two pages of Wikipedia entry on it, list the available libraries for solving the problem, add a couple of examples from their manual, bingo!&lt;br /&gt;&lt;br /&gt;There's also another problem: the code is in Java. This is good for the authors, as it increases the book size and makes it seem much more solid, than in reality. But all the unnecessary cruft like half-line type declarations and bookkeeping code for loops (which amounts to more than half of the code, I'd say) makes it really hard to follow along. You need to really focus to be able to translate the code in mind to some high-level conceptual form before grasping it. (Although, there's a shortcut: just assume, that all the code is mostly irrelevant and skip). It's like the authors used the trick of all students: hide your poor understanding of the subject behind all the recondite words you've heard. So the advice for any algorithmical book: if you want to make the code easy to follow write it in Python or Lisp. At least it would make really apparent, if there's not a lot of essence to it... ;)&lt;br /&gt;&lt;br /&gt;So the book is worth reading only if you're new to the field, don't want to spend time learning the subject, but need to get some working program fast (and don't object using Java for that).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-4592857771883152198?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/4592857771883152198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=4592857771883152198' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/4592857771883152198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/4592857771883152198'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2011/04/book-review-algorithms-of-intelligent.html' title='Book review: Algorithms of the Intelligent Web'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-5684178853198494955</id><published>2011-01-08T09:30:00.006+02:00</published><updated>2011-01-08T09:36:46.386+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>various Lisp stuff</title><content type='html'>In the spirit of &lt;a href="http://xach.livejournal.com/278014.html"&gt;xach's "policy" for half-baked projects&lt;/a&gt; I've also published some of my experiments:&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/vseloved/cl-git-store"&gt;cl-git-store&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/vseloved/asdf2-versions"&gt;ASDF2 broader version support proposal&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;If somebody finds that useful, let me know.&lt;br /&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-5684178853198494955?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/5684178853198494955/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=5684178853198494955' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5684178853198494955'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5684178853198494955'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2011/01/various-lisp-stuff.html' title='various Lisp stuff'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-5109636886300182797</id><published>2010-10-17T15:16:00.002+03:00</published><updated>2010-10-17T15:33:35.921+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Роль CTO в технологической компании</title><content type='html'>Есть известная максима, давно проповедуемая Полом Гремом и взятая на вооружение большей частью американского стартап-сообщества, о том, что технологические стартапы должны иметь минимум двух основателей.  Можно много дискутировать на тему, с чем это связанно: например, есть версия, что "если предприниматель не способен убедить в своей идее хотя бы еще одного человека, то он тем более не сможет убедить в этом рынок".  Я же пришел к выводу, что всё упирается в ответ на вопрос, что можно аутсорсить, а что нет?  Как известно, нельзя этого делать с ключевыми функциями компании.  И первой ключевой функцией является формирование ее идентичности на рынке.  Этим занимается человек, называемый американцами CEO, хотя более правильно было бы назвать его просто лидером проекта.  По своей базе он может быть как инженером, так и маркетологом, финансистом или продажником.  Это не столь важно, поскольку его главная роль, которая заключается в том, чтобы вести компанию в правильном направлении и рассказывать об этом рынку через маркетинг, биздев, продажи, PR и т.д. и т.п., не требует особой специализации.  Очевидно, что при этом он должен стоять у истоков идеи, которая реализуется, и сам крепко верить в нее, иначе он не сможет убедить в ней других.  И эта роль настолько обширна, что она должна быть единственной сферой ответственности такого человека.  Да, он может быть крутым программистом и вносить свой вклад, а, воможно, и наибольшее ноу-хау в технологии своей компании, но он не может быть ответственным за эту сферу в целом.  Потому что, если отвечать за два дела одновременно, то ни одно из них не выйдет сделать хорошо.  И это показывает опыт, в том числе и мой собственный.&lt;br /&gt;&lt;br /&gt;Вот здесь и выходит на арену CTO, роль которого в компании заключается в технологическом лидерстве.  Часто считается, что СТО должен написать основную часть кода, если это касается софтверного стартапа.  Это не обязательно.  Код могут написать хоть 10 фрилансеров или 100 миллионов обезьян, поскольку это довольно стандартная работа, и если правильно поставить задачу, с ней прекрасно справится наемный сотрудник.  Но вот, что сложно — так это поставить задачу и сформулировать рамки, в которых должен развиваться проект, а потом следить за тем, чтобы они соблюдались.  В технологическом бизнесе, как ни крути, а основные риски все-таки технологические.  И все остальные участники процесса (наемные сотрудники, клиенты и т.д.) будут их только увеличивать.  Единственный, кто может и должен с ними бороться — это СТО.&lt;br /&gt;&lt;br /&gt;СТО — это роль того, кто принимает окончательное решение, выбрать технологию Х, за которую выступает разработчик, или Y, которая сейчас на волне хайпа.  Это тот, кто не спит ночами из-за того, что не настроен бекап базы данных.  Это тот, кто вводит практику ревью кода и добивается ее реального воплощения.  Тот, кто отвечает программистам на вопросы: "зачем?", "почему?" и "когда?"  Тот, кто отбирает подходящих инженеров.  И принимает еще 100500 разных решений, а потом добивается их исполнения.  В общем, это тот, кто создает технологическую культуру компании и не дает появляться технологическуму долгу, который в будущем (и, скорее, близком, чем отдаленном) потянет компанию ко дну или же не даст ей развиваться необходимыми темпами.&lt;br /&gt;&lt;br /&gt;Так что правило про двух основателей я переформулировал бы так.&lt;br /&gt;&lt;blockquote&gt;На этапе становления у технологического бизнеса есть 2 ключевые роли, которые должны выполнять 2 разных основателя компании: бизнес-лидер и технолонический лидер.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-5109636886300182797?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/5109636886300182797/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=5109636886300182797' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5109636886300182797'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5109636886300182797'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/10/cto.html' title='Роль CTO в технологической компании'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-3037237616891474492</id><published>2010-08-13T17:57:00.009+03:00</published><updated>2010-08-14T09:47:45.516+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>ASDF как язык описания систем</title><content type='html'>Это третья статья в серии про ASDF.  Первая часть — &lt;a href="http://lisp-univ-etc.blogspot.com/2010/06/asdf-2.html"&gt;об ASDF 2&lt;/a&gt;, вторая — &lt;a href="http://lisp-univ-etc.blogspot.com/2010/07/asdf.html"&gt;об архитектуре ASDF&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Как было замечено &lt;a href="http://tream.dreamhosters.com/tream/musings/49-lisp/76-analysis-of-existing-asdf-files"&gt;ранее&lt;/a&gt;, ASDF выполняет 2 связанные, но все же довольно разные по требованиям со стороны пользователей функции: описание систем и управление их сборкой.  В этой статье я постараюсь перечислить распространенные (и не очень) шаблоны его применения для этих задач, собранные мной из более 70 open-source Lisp библиотек, с которыми приходилось работать.  Я думаю, что систематизация этих знаний сослужит хорошую службу Lisp-сообществу и будет полезна как начинающим, так и экспертам, которые смогут усовешенствовать свои техники.&lt;br /&gt;&lt;br /&gt;Изначально в этой статье я планировал разобрать все основные шаблоны использования ASDF, но материала оказалось слишком много, поэтому более сложным темам его применения как build-инструмента будет посвящена следующая серия.&lt;br /&gt;&lt;br /&gt;Прямые ссылки на полезные вещи:&lt;ul&gt;&lt;li&gt;&lt;a href="#tutorial"&gt;новый проект c помощью ASDF за 2 минуты&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#defsystem-package"&gt;пакет для defsystem-формы&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#proper-symbols"&gt;правильные символы в именах систем и пакетов&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#system-name-path"&gt;имена и пути компонент&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#platform-dep"&gt;платформо-зависимые описания систем&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;Начало работы с ASDF&lt;/h2&gt;&lt;br /&gt;ASDF нельзя назвать инструментом, который можно освоить за 5 минут.  Я сам, например, очень долго вникал в идеологию и особенности его работы, и делал это в основном методом проб и ошибок (мануал мне плохо помогал :).  Однако, сейчас мне кажется, что эти трудности связанны не с особенностями ASDF (какими-то неудачными архитектурными решениями и т.п.) или Lisp'а, а, в первую очередь, с отсутствием должной документации и best-practices.  Именно этот пробел я и хочу заполнить. Да, всё, что будет показано ниже, почерпнуто из широкодоступного кода Lisp-библиотек, однако часто нужны скорее tutorial'ы, которые позволят быстро начать работу и сразу получить ожидаемый результат.  Поэтому начнем именно с такого самого простого примера.&lt;br /&gt;&lt;br /&gt;&lt;a name="tutorial"&gt;&lt;/a&gt;Итак, чтобы создать новый проект с помощью ASDF — нужно создать директорию проекта (пусть будет &lt;code&gt;testprj&lt;/code&gt;), в которой поместить имеющиеся lisp-исходники (пусть это будет файл &lt;code&gt;app.lisp&lt;/code&gt;) и создать в этой директории файл &lt;code&gt;testprj.asd&lt;/code&gt;, в который поместить следующую форму:&lt;code&gt;&lt;pre&gt;(asdf:defsystem #:testprj&lt;br /&gt;  :components ((:file "app")))&lt;/pre&gt;&lt;/code&gt;Для загрузки нового проекта в Lisp-среду нужно, чтобы наш &lt;code&gt;testprj.asd&lt;/code&gt; файл был в известных ASDF местах.  На данный момент поддерживается 2 способа задания таких мест: аналог PATH (&lt;code&gt;*central-registry*&lt;/code&gt;) и &lt;code&gt;source-registry&lt;/code&gt; (использующий конфигурационные файлы).  Самый распространенный подход (в POSIX-окружении) — это создать ссылку на asd-файл в какой-то специальной директории (например, &lt;code&gt;~/.lisp&lt;/code&gt;), которая добавлена в &lt;code&gt;*central-registry*&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;В общем полная настройка такого варианта выглядит так:&lt;code&gt;&lt;pre&gt;$ ln -s &amp;lt;path-to-testprj.asd&amp;gt; ~/.lisp/testprj.asd&lt;br /&gt;&lt;br /&gt;;; обычно это делается в rc-файле lisp'а (например, .sbclrc)&lt;br /&gt;CL-USER&gt; (push "~/.lisp/" asdf:*central-registry*)&lt;br /&gt;&lt;br /&gt;;; далее можно выполнять (для ASDF 2):&lt;br /&gt;CL-USER&gt; (asdf:load-system :testprj)&lt;br /&gt;;; или для любой версии ASDF:&lt;br /&gt;CL-USER&gt; (asdf:oos 'asdf:load-op :testprj)&lt;br /&gt;;; или в большинстве окружений (например, SBCL), даже:&lt;br /&gt;CL-USER&gt; (require :testprj)&lt;/pre&gt;&lt;/code&gt;Вот и всё.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Другие формы в ASD-файле&lt;/h3&gt;&lt;br /&gt;На самом деле, ASD-файл — это обычный lisp-исходник, просто с другим расширением, что помогает найти его ASDF'у, поэтому в нем могут находится и другие lisp-формы.  Впрочем, прежде, чем помещать туда произвольные формы, стоит очень хорошо подумать.  Во всяком случае, ASD-файл следует сохранить полностью декларативным, поскольку большинство инcтрументов, созданных вокруг ASDF, должны иметь возможность просто читать этот файл, не исполняя.&lt;br /&gt;&lt;h3&gt;&lt;a name="defsystem-package"&gt;&lt;/a&gt;Пакет для &lt;code&gt;defsystem&lt;/code&gt;&lt;/h3&gt;&lt;br /&gt;Единственный класс форм, размещение которых в ASD-файле необходимо (помимо &lt;code&gt;defsystem&lt;/code&gt; и некоторых других ASDF-специфичных форм, о которых будет сказано далее) — это формы работы с пакетом.&lt;br /&gt;&lt;br /&gt;Сейчас в Lisp-сообществе есть 2 конкурирующих взгляда на то, как это делать:&lt;ul&gt;&lt;li&gt;простой — определять системы в пакете ASDF &lt;code&gt;(in-package :asdf)&lt;/code&gt;&lt;/li&gt;&lt;li&gt;скурпулезный — определять отдельный пакет только для ASDF-описания системы:&lt;code&gt;&lt;pre&gt;;; Пример из описания системы ARCHIVE:&lt;br /&gt;(defpackage :archive-system (:use :cl :asdf))&lt;br /&gt;(in-package :archive-system)&lt;/pre&gt;&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;Лично я являюсь сторонником первого подхода, поскольку он не добавляет избыточной сложности.  Его единственный недостаток — в возможном конфликте символов, однако он решается использованием особых символов (&lt;code&gt;#:testprj&lt;/code&gt; или &lt;code&gt;:testprj&lt;/code&gt;).  Единственным случаем, когда вариант отдельного пакета может оказаться препочтительнее — это какие-то очень сложные описания систем с зависимостями от дополнительных пакетов.  Впрочем, это верный признак того, что вы делаете что-то не так и, просто, не пользуетесь всеми возможностями ASDF.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a name="proper-symbols"&gt;&lt;/a&gt;Использование символов&lt;/h3&gt;&lt;br /&gt;Имена ASDF-систем в форме &lt;code&gt;defsystem&lt;/code&gt;, как и CL пакетов в &lt;code&gt;defpackage&lt;/code&gt;, можно задавать несколькими способами:&lt;ul&gt;&lt;li&gt;символом: &lt;code&gt;(defsystem testprj ...)&lt;/code&gt;&lt;/li&gt;&lt;li&gt;кивордом: &lt;code&gt;(defsystem :testprj ...)&lt;/code&gt;&lt;/li&gt;&lt;li&gt;неинтернированным символом: &lt;code&gt;(defsystem #:testprj ...)&lt;/code&gt;&lt;/li&gt;&lt;li&gt;строкой: &lt;code&gt;(defsystem "TESTPRJ" ...)&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;Мне не сразу стал понятен принцип выбора, но он прост: нужно использовать неинтернированные символы (а все остальные встречающиеся варианты связанны с тем, что авторы просто не знали или не понимали этот вариант :).&lt;br /&gt;&lt;br /&gt;Вариант строки, в принципе, эквивалентен, но не эстетичен, киворда — приводит к "засорению" пакета keywords,— а просто символа — подвержен риску конфликта имен (его точно не стоит использовать, если описывать систему внутри ASDF-пакета).&lt;br /&gt;&lt;h2&gt;Задание зависимостей&lt;/h2&gt;&lt;br /&gt;Я бы сказал, что ключевой функцией ASDF является разрешени зависимостей между исходными файлами в рамках одной системы и между разными системами.  Все зависимости в ASDF задаются ключевым словом &lt;code&gt;:depends-on&lt;/code&gt; в описании соответствующего компонента (файла, модуля, системы и т.д.)&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Зависимости от других систем&lt;/h3&gt;&lt;br /&gt;Разумеется, любая серьезная система существует не в вакууме, а использует множество других библиотек.  Несмотря на миф об их отсутствии в lisp-экосистеме :), большие проекты, над которыми мне приходилось работать, как правило, использовали порядка 20-30 сторонних библиотек (включая рекурсивные зависимости).&lt;br /&gt;&lt;br /&gt;Предположим, что наш проект использует библиотеку утилит &lt;code&gt;RUTILS&lt;/code&gt;.  В таком случае нам нужно немного расширить его описание:&lt;code&gt;&lt;pre&gt;(asdf:defsystem #:testprj&lt;br /&gt;  :depends-on (#:rutils)&lt;br /&gt;  :components ((:file "app")))&lt;/pre&gt;&lt;/code&gt;Опять же для задания имени зависимости мы используем неинтернированный символ.  ASDF позволяет дать и более точное описание такой зависимости (которое пока используется крайне редко, и о котором в отдельной статье, посвященной версиям).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Зависимости между файлами&lt;/h3&gt;&lt;br /&gt;Если в проекте больше одного lisp-файла, то стандартной практикой является добавления файла &lt;code&gt;packages.lisp&lt;/code&gt;, в котором описываются 1 или несколько пакетов, которые будут использоваться (создавать любой неигрушечный проект в рамках &lt;code&gt;cl-user&lt;/code&gt; пакета строго не рекомендуется :).&lt;br /&gt;&lt;br /&gt;Предположим, что помимо &lt;code&gt;app.lisp&lt;/code&gt;, мы также используем файл &lt;code&gt;support.lisp&lt;/code&gt;.  В таком случае наше описание приобритет следующую форму:&lt;code&gt;&lt;pre&gt;(asdf:defsystem #:testprj&lt;br /&gt;  :depends-on (#:rutils)&lt;br /&gt;  :components ((:file "packages")&lt;br /&gt;               (:file "support")&lt;br /&gt;               (:file "app")))&lt;/pre&gt;&lt;/code&gt;Однако мы не задалт порядок загрузки отдельных файлов, что может привести к неожиданным результатам (скажем, Lisp будет ругаться на форму &lt;code&gt;(in-package #:testprj)&lt;/code&gt; в файле &lt;code&gt;support.lisp&lt;/code&gt;, поскольку файл &lt;code&gt;packages.lisp&lt;/code&gt; еще не загружен. Поэтому эту форму нужно доработать:&lt;code&gt;&lt;pre&gt;(asdf:defsystem #:testprj&lt;br /&gt;  :depends-on (#:rutils)&lt;br /&gt;  :components ((:file "packages")&lt;br /&gt;               (:file "support" :depends-on "packages")&lt;br /&gt;               (:file "app" :depends-on "support")))&lt;/pre&gt;&lt;/code&gt;Тут мы не пишем, что &lt;code&gt;app&lt;/code&gt; зависит от &lt;code&gt;packages&lt;/code&gt;, поскольку зависимости транзитивны.&lt;br /&gt;&lt;br /&gt;Такой последовательный (serial) вариант зависимостей характерен для доброй половины проектов, поэтому для него есть специальная декларация для &lt;code&gt;defsystem&lt;/code&gt;: &lt;code&gt;:serial t&lt;/code&gt;.  С ней наше описание снова упростится:&lt;code&gt;&lt;pre&gt;(asdf:defsystem #:testprj&lt;br /&gt;  :depends-on (#:rutils)&lt;br /&gt;  :serial t&lt;br /&gt;  :components ((:file "packages")&lt;br /&gt;               (:file "support")&lt;br /&gt;               (:file "app")))&lt;/pre&gt;&lt;/code&gt;В основе ASDF лежит расширяемая объектная модель компонентов и операций над ними.  Некоторые разработчики используют в описании систем прямое имя класса &lt;code&gt;:cl-source-file&lt;/code&gt;, а не &lt;code&gt;:file&lt;/code&gt;.  А вот класса &lt;code&gt;file&lt;/code&gt; в ASDF как раз нет: конкретный класс определяется из слота &lt;code&gt;default-component-class&lt;/code&gt; модуля (система — потомок модуля) и по умолчанию, конечно, является как раз lisp-исходником.&lt;br /&gt;&lt;br /&gt;Помимо этого описан ряд других вариантов, компонент, а также всегда можно описать свой собственный (об этом — в следующей статье).&lt;br /&gt;&lt;br /&gt;Например, интересной практикой является подобная декларация компонента:&lt;code&gt;&lt;pre&gt;:components ((:static-file "cl-oauth.asd")&lt;br /&gt;             ...&lt;/pre&gt;&lt;/code&gt;(&lt;code&gt;static-file&lt;/code&gt; — это любой статичный файл, который не обрабатывается компилятором, например файл лицензии или, как в данном случае, собственно файл описания системы — ведь он обрабатывается только ASDF.  Зачем его добавлять?  Например, если мы расчитываем, что будем реализовывать какую-нибудь операцию, типа &lt;code&gt;publish-op&lt;/code&gt;, для создания дистрибутива из исходных файлов).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Модули&lt;/h3&gt;&lt;br /&gt;Модули ASDF — это логические компоненты системы, которые объединяют несколько других компонент.  Использование модулей позволяет решить 2 задачи:&lt;ul&gt;&lt;li&gt;аггрегированно управлять зависимостями&lt;br/&gt;&lt;br /&gt;Скажем, у нас есть 2 части системы: бэкенд и фронтенд, которые зависят от общего файла утилит.  И при изменении каждой из них мы не хотим перекомпилировать другую.  В таком случае логично будет описать каждую часть в виде отдельного модуля&lt;/li&gt;&lt;li&gt;распределить исходники по разным директориям (в какой-то степени это аналог модулей в Python, но без управления видимостью — об этом в следующей статье)&lt;code&gt;&lt;pre&gt;(defsystem :arnesi&lt;br /&gt;  ...&lt;br /&gt;  :components&lt;br /&gt;  ((:module :src&lt;br /&gt;            :components ((:file "accumulation"&lt;br /&gt;                                :depends-on ("packages" "one-liners"))&lt;br /&gt;                         (:file "asdf" :depends-on ("packages" "io"))&lt;br /&gt;                         (:file "csv" :depends-on ("packages" "string"))&lt;br /&gt;                         (:file "compat" :depends-on ("packages"))&lt;br /&gt;                         (:module :call-cc&lt;br /&gt;                                  :components ((:file "interpreter")&lt;br /&gt;                                               (:file "handlers")&lt;br /&gt;                                               (:file "apply")&lt;br /&gt;                                               (:file "generic-functions")&lt;br /&gt;                                               (:file "common-lisp-cc"))&lt;br /&gt;                                  :serial t&lt;br /&gt;                                  :depends-on ("packages" "walk"&lt;br /&gt;                                               "flow-control" "lambda-list"&lt;br /&gt;                                               "list" "string"&lt;br /&gt;                                               "defclass-struct"))))&lt;br /&gt;   ...))&lt;/pre&gt;&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;А можно ли разложить файлы по разным директориям не привязываясь к модели зависимостей, которая накладывается модулями?  Да.  У любого компонента есть слот &lt;code&gt;:pathname&lt;/code&gt;, который позволяет явно задать путь к нему.  Однако его использование имеет свои особенности — об этом дальше.&lt;br /&gt;&lt;h2&gt;&lt;a name="system-name-path"&gt;&lt;/a&gt;Имена компонент и путь к ним&lt;/h2&gt;&lt;br /&gt;У любого ASDF-компонента есть обязательный аттрибут имя, который используется при его поиске и разрешении зависимотстей.  Однако этот аттрибут *не задается* декларацией &lt;code&gt;:name&lt;/code&gt; в описании компонента.&lt;code&gt;&lt;pre&gt;(defsystem :ch-image&lt;br /&gt;   :name "ch-image"&lt;br /&gt;   ...)&lt;/pre&gt;&lt;/code&gt;В этом коде &lt;code&gt;:name "ch-image"&lt;/code&gt; несет чисто эстетический смысл (&lt;a href="#foot-1"&gt;не верите?&lt;/a&gt; :)&lt;br /&gt;&lt;br /&gt;Имя задается первым символом в декларации компонента: в данном случае &lt;code&gt;ch-image&lt;/code&gt;, или же в случае модуля выше — &lt;code&gt;:src&lt;/code&gt;, или же &lt;code&gt;"accumulation"&lt;/code&gt; для файла там же.  Все внутренние функции ASDF умеют работать как с символьным, так и со строковым представлением имен, описанных &lt;a href="#proper-symbols"&gt;выше&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Кроме того, у каждого компонента есть аттрибут &lt;code&gt;pathname&lt;/code&gt;, который определяет его положение в файловой системе.  Однако, в отличие от имени, его как раз можно задать соответствующей декларацией.  Например, этот пример задает относительный собственно ASD-файла путь к модулю &lt;code&gt;io.multiplex&lt;/code&gt; библиотеки &lt;code&gt;IOLIB&lt;/code&gt;: &lt;code&gt;:pathname (merge-pathnames #p"io.multiplex/" *load-truename*)&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Если же путь не задавать явно, то он вычисляется из имени, расширения (которое определяется типом компонента) и положения в иерархии модулей.  Таким образом для примера задания модуля в &lt;code&gt;arnesi&lt;/code&gt; (выше) для компонента &lt;code&gt;:file "interpreter"&lt;/code&gt; будет вычислен такой путь: &lt;code&gt;src/call-cc/interpreter.lisp&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Ну и ответ на вопрос, как разбросать файлы по директориям, не используя модули: задать для всех файлов явный &lt;code&gt;:pathname&lt;/code&gt;.&lt;br /&gt;&lt;h2&gt;Мета-информация&lt;/h2&gt;&lt;br /&gt;&lt;code&gt;Defsystem&lt;/code&gt;-форма позволяет задать большое количество полезных метаданных для системы.  Очень важно указать как минимум следующие:&lt;ul&gt;&lt;li&gt;&lt;code&gt;:version&lt;/code&gt;, например &lt;code&gt;:version "0.0.1"&lt;/code&gt; (подробнее о версиях — в отдельной статье)&lt;/li&gt;&lt;li&gt;&lt;code&gt;:author&lt;/code&gt; или &lt;code&gt;:maintainer&lt;/code&gt; (с указанием e-mail'а, чтобы к вам впоследствии смогли обратиться и предложить миллионы за доработку и поддержку вашей прекрасной библиотеки :)&lt;/li&gt;&lt;li&gt;&lt;code&gt;:licence&lt;/code&gt; — чтобы люди знали, как они могут пользоваться вашими поделками&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;&lt;a name="platform-dep"&gt;&lt;/a&gt;Платформо-зависимое описание систем&lt;/h2&gt;&lt;br /&gt;CL предоставляет исключительно удобный механизм условной компиляции и выполнения кода (&lt;code&gt;#+/#-&lt;/code&gt;).  И как раз в описаниях систем он, разумеется, находит широкое применение:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;предотвращение загрузки системы в целом — тут интересны примеры из двух альтернативных библиотек для FFI:&lt;code&gt;&lt;pre&gt;#+(or allegro lispworks cmu openmcl digitool cormanlisp sbcl scl)&lt;br /&gt;(defsystem uffi ...)&lt;br /&gt;&lt;br /&gt;;; CFFI: этот вариант, безусловно, правильнее, чем просто тихо ничего не сделать&lt;br /&gt;#-(or openmcl sbcl cmu scl clisp lispworks ecl allegro cormanlisp)&lt;br /&gt;(error "Sorry, this Lisp is not yet supported.  Patches welcome!")&lt;/pre&gt;&lt;/code&gt;&lt;/li&gt;&lt;li&gt;вынесение функций, зависящих от конкретной lisp-среды в отдельные файлы — пример из все того же CFFI:&lt;code&gt;&lt;pre&gt;:components (#+openmcl    (:file "cffi-openmcl")&lt;br /&gt;             #+sbcl       (:file "cffi-sbcl")&lt;br /&gt;             #+cmu        (:file "cffi-cmucl")&lt;br /&gt;             #+scl        (:file "cffi-scl")&lt;br /&gt;             #+clisp      (:file "cffi-clisp")&lt;br /&gt;             #+lispworks  (:file "cffi-lispworks")&lt;br /&gt;             #+ecl        (:file "cffi-ecl")&lt;br /&gt;             #+allegro    (:file "cffi-allegro")&lt;br /&gt;             #+cormanlisp (:file "cffi-corman")&lt;/pre&gt;&lt;/code&gt;&lt;/li&gt;&lt;li&gt;закладка нескольких вариантов построения библиотеки, в зависимсоти от каких-то условий.  Тут проще всего привести примеры:&lt;code&gt;&lt;pre&gt;;; использовать ли acl-regexp2-engine?&lt;br /&gt;(defsystem :cl-ppcre&lt;br /&gt;  :version "2.0.3"&lt;br /&gt;  :serial t&lt;br /&gt;  :components ((:file "packages")&lt;br /&gt;               (:file "specials")&lt;br /&gt;               (:file "util")&lt;br /&gt;               (:file "errors")&lt;br /&gt;               (:file "charset")&lt;br /&gt;               (:file "charmap")&lt;br /&gt;               (:file "chartest")&lt;br /&gt;               #-:use-acl-regexp2-engine&lt;br /&gt;               (:file "lexer")&lt;br /&gt;               #-:use-acl-regexp2-engine&lt;br /&gt;               (:file "parser")&lt;br /&gt;               #-:use-acl-regexp2-engine&lt;br /&gt;               (:file "regex-class")&lt;br /&gt;               #-:use-acl-regexp2-engine&lt;br /&gt;               (:file "regex-class-util")&lt;br /&gt;               #-:use-acl-regexp2-engine&lt;br /&gt;               (:file "convert")&lt;br /&gt;               #-:use-acl-regexp2-engine&lt;br /&gt;               (:file "optimize")&lt;br /&gt;               #-:use-acl-regexp2-engine&lt;br /&gt;               (:file "closures")&lt;br /&gt;               #-:use-acl-regexp2-engine&lt;br /&gt;               (:file "repetition-closures")&lt;br /&gt;               #-:use-acl-regexp2-engine&lt;br /&gt;               (:file "scanner")&lt;br /&gt;               (:file "api")))&lt;/pre&gt;&lt;/code&gt;&lt;code&gt;&lt;pre&gt;;; какие бэкенды генерации графических файлов доступны?&lt;br /&gt;(defsystem :ch-image&lt;br /&gt;   ...&lt;br /&gt;   (:module&lt;br /&gt;    :io&lt;br /&gt;    :components&lt;br /&gt;    (#+ch-image-has-tiff-ffi&lt;br /&gt;     (:cl-source-file "tiffimage")&lt;br /&gt;     #+ch-image-has-cl-jpeg&lt;br /&gt;     (:cl-source-file "jpegimage")&lt;br /&gt;     #+(and ch-image-has-zpng)&lt;br /&gt;     (:cl-source-file "pngimage")&lt;br /&gt;     (:cl-source-file "imageio"&lt;br /&gt;                      :depends-on (#+ch-image-has-tiff-ffi&lt;br /&gt;                                   "tiffimage"&lt;br /&gt;                                   #+ch-image-has-cl-jpeg&lt;br /&gt;                                   "jpegimage")))&lt;br /&gt;    :depends-on (:src))&lt;/pre&gt;&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;a name="foot-1"&gt;&lt;/a&gt;&lt;code&gt;&lt;pre&gt;;; :name не имеет значения&lt;br /&gt;CL-USER&gt; (defsystem :ch-image&lt;br /&gt;           :name "ch-image1")&lt;br /&gt;#&amp;lt;SYSTEM "ch-image" {AA7A911}&amp;gt;&lt;br /&gt;CL-USER&gt; (describe *)&lt;br /&gt;#&amp;lt;SYSTEM "ch-image" {AA7A911}&amp;gt;&lt;br /&gt;  [standard-object]&lt;br /&gt;&lt;br /&gt;Slots with :INSTANCE allocation:&lt;br /&gt;  NAME                     = "ch-image"&lt;br /&gt;  VERSION                  = #&amp;lt;unbound slot&amp;gt;&lt;br /&gt;  IN-ORDER-TO              = NIL&lt;br /&gt;  DO-FIRST                 = ((COMPILE-OP (LOAD-OP)))&lt;br /&gt;  INLINE-METHODS           = NIL&lt;br /&gt;  PARENT                   = NIL&lt;br /&gt;  RELATIVE-PATHNAME        = #P"/home/vs/lib/lisp/ch-image_0.4.1/"&lt;br /&gt;  OPERATION-TIMES          = #&amp;lt;HASH-TABLE :TEST EQL :COUNT 0 {AA7AB61}&amp;gt;&lt;br /&gt;  PROPERTIES               = NIL&lt;br /&gt;  COMPONENTS               = NIL&lt;br /&gt;  IF-COMPONENT-DEP-FAILS   = :FAIL&lt;br /&gt;  DEFAULT-COMPONENT-CLASS  = NIL&lt;br /&gt;  DESCRIPTION              = #&amp;lt;unbound slot&amp;gt;&lt;br /&gt;  LONG-DESCRIPTION         = #&amp;lt;unbound slot&amp;gt;&lt;br /&gt;  AUTHOR                   = #&amp;lt;unbound slot&amp;gt;&lt;br /&gt;  MAINTAINER               = #&amp;lt;unbound slot&amp;gt;&lt;br /&gt;  LICENCE                  = #&amp;lt;unbound slot&amp;gt;&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-3037237616891474492?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/3037237616891474492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=3037237616891474492' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3037237616891474492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3037237616891474492'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/08/asdf.html' title='ASDF как язык описания систем'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-2570599125854865792</id><published>2010-07-09T12:18:00.005+03:00</published><updated>2011-04-07T20:00:43.936+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Новости Redis и cl-redis</title><content type='html'>Наконец, дошли руки обновить наш &lt;a href="http://github.com/vseloved/cl-redis"&gt;клиент для Redis&lt;/a&gt; и доработать его для поддержки новых режимов, которые добавились в последних версиях БД.  Заодно и проверить утверждения из &lt;a href="http://fprog.ru/2010/issue5/vsevolod-dyomkin-lisp-philosophy/#htoc2"&gt;статьи&lt;/a&gt; о том, что клиент тривиально расширяемый и легко адаптируется к изменениям.&lt;br /&gt;&lt;br /&gt;На самом деле, пришлось немного повозиться.  Но не потому, что утверждения ошибочны, а из-за стандартной проблемы подавляющего большинства софтверных проектов — неадекватной документации.  Впрочем, в случае с Redis'ом все не однозначно.  С одной стороны, в общем, документация хорошая и правильная: во-первых, не перегруженная, во-вторых, описан протокол взаимодействия, и для каждой команды более-менее указано, как она использует протокол (а еще и другие полезные вещи: иногда примеры использования, а также, что мне понравилось, временные характеристики).  Но, в том то и дело, что "более-менее" указано, и некоторые важные моменты упущены, так что в этот раз пришлось лезть в код родного клиента &lt;code&gt;redis-cli&lt;/code&gt; и даже прослушивать его взаимодействие с сервером &lt;code&gt;tcpdump&lt;/code&gt;'ом (жалко, что я не додумался с этого начать :)&lt;br /&gt;&lt;br /&gt;Короче говоря, у Redis появилась поддержка хеш-таблиц, но обнаружился &lt;a href="http://code.google.com/p/redis/issues/detail?id=275"&gt;баг&lt;/a&gt; у всех команд, которые передают ключи (в терминах Redis — "поля") в таблице (самый простой пример: &lt;code&gt;&lt;nobr&gt;HGET table field&lt;/nobr&gt;&lt;/code&gt;).  Почему этого не заметили разработчики?  Видимо, потому, что родной клиент использует для общения с сервером не &lt;a href="http://code.google.com/p/redis/wiki/ProtocolSpecification"&gt;описанный протокол&lt;/a&gt; с разными вариантами передачи команд: &lt;code&gt;inline&lt;/code&gt;, &lt;code&gt;bulk&lt;/code&gt; и т.п.,— а простой унифицированный способ в форме &lt;a href="http://code.google.com/p/redis/wiki/ProtocolSpecification#Multi_bulk_commands"&gt;&lt;code&gt;multi-bulk&lt;/code&gt;&lt;/a&gt;, т.е. вместо строки &lt;code&gt;&lt;nobr&gt;"HGET table field"&lt;/nobr&gt;&lt;/code&gt;, которую посылал наш клиент, вот что:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;"*3&lt;br /&gt;$4&lt;br /&gt;HGET&lt;br /&gt;$5&lt;br /&gt;table&lt;br /&gt;$5&lt;br /&gt;field"&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Соответственно, наверно, и не заметили особенность при обработке полей в случае передачи команды в &lt;code&gt;inline&lt;/code&gt;-форме.&lt;br /&gt;&lt;br /&gt;Справедливости ради, нужно сказать, что если порыться в документации, то можно найти замечание, что &lt;code&gt;"A good client library may implement unknown commands using this command format in order to support new commands out of the box without modifications."&lt;/code&gt; (т.е. самый продуктивный путь был — снова перечитать спецификацию протокола :)  Теперь вот, задумался, может стоит перевести все команды на эту &lt;code&gt;multi-bulk&lt;/code&gt; форму.  И API упростится, будет не:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(def-cmd HGET (key field)&lt;br /&gt;"Retrieve the value of the specified hash FIELD."&lt;br /&gt;:generic :bulk)&lt;/code&gt;&lt;/pre&gt;а&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(def-cmd HGET (key field) :bulk&lt;br /&gt;"Retrieve the value of the specified hash FIELD.")&lt;/code&gt;&lt;/pre&gt;С другой стороны, немного уменьшиться производительность.&lt;br /&gt;&lt;br /&gt;Вообще, Redis все в большем количестве мест использует &lt;code&gt;multi-bulk&lt;/code&gt; форму (поскольку она наиболее общая).  Поддержка PubSub сделана также на ней, хотя и с небольшим отклонением.&lt;br /&gt;&lt;br /&gt;PubSub — это сейчас такой горячий пирожок, который все хотят съесть.  Как это реализовано здесь?  Есть команды &lt;code&gt;SUBSCRIBE&lt;/code&gt; и &lt;code&gt;PSUBSCRIBE&lt;/code&gt;, позволяющие подписаться на каналы соответственно по имени и по шаблону (типа &lt;code&gt;"news.*"&lt;/code&gt;).  И есть &lt;code&gt;PUBLISH&lt;/code&gt;, которая посылает сообщения.  Сообщения доставляются условно мгновенно и приходят в рамках тех &lt;b&gt;активных&lt;/b&gt; соединений, в которых произведена подписка.  Таким образом, типичный подход к применению этого, насколько я понимаю — это когда у нас есть отдельная нить-слушатель, которая обрабатывает приходящие сообщения и иницирует какую-то реакцию в программе.  Где-то так:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(with-connection ()&lt;br /&gt;(red-subscribe "chan")&lt;br /&gt;(loop&lt;br /&gt;   (let ((msg (expect :multi)))&lt;br /&gt;     (bt:make-thread (lambda ()&lt;br /&gt;                       (process (elt msg 2))))&lt;br /&gt;     ;; или же, например&lt;br /&gt;     (bt:with-lock-held (*lock*)&lt;br /&gt;       (push (elt msg 2) *internal-queue*)))))&lt;/code&gt;&lt;/pre&gt;Сообщения имеют вид &lt;code&gt;'("message" &amp;lt;канал&amp;gt; &amp;lt;сообщение&amp;gt;)&lt;/code&gt; для простой подписки и &lt;code&gt;'(&amp;lt;шаблон&amp;gt; &amp;lt;канал&amp;gt; &amp;lt;сообщение&amp;gt;)&lt;/code&gt; для &lt;code&gt;PSUBSCRIBE&lt;/code&gt;.  Вот, собственно, и всё.&lt;br /&gt;&lt;br /&gt;Как видно из примера, получение сообщений из канала блокирующее и выполняется просто за счет вызова функции &lt;code&gt;expect&lt;/code&gt;, которая во всех обычных командах присутствует в паре &lt;code&gt;tell-expect&lt;/code&gt;.  Так что, возвращаясь к моим утверждениям про легкую расширяемость и адаптацию — они подтвердились (и один из примеров — вот он).&lt;br /&gt;&lt;br /&gt;Redis действительно быстро развивается и много чего меняется, добавляются радикально новые варианты использования: в данном случае транзакции и PubSub.  Но для поддержки всего этого в клиенте хватает точечных изменений на уровне реализации протокола, никакого рефакторинга.  В этот раз нужно было добавить еще несколько вариантов ожидания ответа: &lt;code&gt;:queued&lt;/code&gt; (для транзакций), при котором считывается сразу несколько разнородных ответов подряд; &lt;code&gt;:float&lt;/code&gt;; а также &lt;code&gt;:pubsub&lt;/code&gt;,— поменять несколько определений команд, потому что поменялась сама их спецификация.  Ну и добавилась обработка особого случая транзакций, когда любая команда возвращает &lt;code&gt;"QUEUED"&lt;/code&gt; вместо своего стандартного ответа.&lt;br /&gt;&lt;br /&gt;PS. Да, и еще про транзакции: теперь, как видите, Redis и их &lt;a href="http://code.google.com/p/redis/wiki/MultiExecCommand"&gt;поддерживает&lt;/a&gt;.  Что меня заинтересовало — это обсуждение гарантий целостности (на этой же странице внизу), которые, на первый взгляд, недостаточны: нет условия успешного завершения всех команд в рамках транзакции, чтобы транзакция была признана успешной.  Т.е. &lt;code&gt;ROLLBACK&lt;/code&gt; не предусмотрен.  Но вот, что пишет на этот счет Сальваторе Санфилиппо:&lt;br /&gt;&lt;blockquote&gt;I think you are missing the point about &lt;code&gt;MULTI/EXEC&lt;/code&gt;, that is, a Redis command only fails on syntax error or type mismatch. That is, in code without errors the transaction will either be executed as whole or nothing. Still it is true that if there are programming errors like using a list command against a set, or a syntax error, only the sane commands will be performed and the others instead will fail, but it's very hard for this to happen in production.&lt;/blockquote&gt;&lt;br /&gt;Так что транзакции в Redis кислотные по-своему, и нужно хорошо уяснить для себя их семантику, прежде чем браться применять.  (И опять, возвращаясь к тому, с чего я начинал: встает проблема адекватности документации.  А идеальная документация — исполняемая... ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-2570599125854865792?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/2570599125854865792/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=2570599125854865792' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/2570599125854865792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/2570599125854865792'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/07/redis-cl-redis.html' title='Новости Redis и cl-redis'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-7566368012936801455</id><published>2010-07-09T07:58:00.006+03:00</published><updated>2010-07-09T12:45:25.249+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Внутреннее устройство ASDF</title><content type='html'>Это вторая статья в серии про ASDF.  Первая рассказывала про нововведения в &lt;a href="http://lisp-univ-etc.blogspot.com/2010/06/asdf-2.html"&gt;ASDF 2&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Итак, рассказ о внутренностях ASDF начнем с того, что меня самого испугала бы задача создать с нуля подобную систему.  В данном случае в идеале нужно единомоментно получить программу, обладающую одновременно такими довольно противоречивыми характеристиками:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;хорошо покрывающую основные варианты использования (в случае ASDF — это и средство описания систем (для их последующего распространения), и менеджер сборки)&lt;/li&gt;&lt;li&gt;простую и удобную для непосвященных в детали пользователей&lt;/li&gt;&lt;li&gt;хорошо расширяемую для того, чтобы позволить развивать сопутствующую инфраструктуру (например, такие средства как ASDF-INSTALL)&lt;/li&gt;&lt;li&gt;ну и, разумеется, сразу корректно работающую&lt;/li&gt;&lt;/ul&gt;ASDF была впервые написана Деном Барлоу в 2001 году.  Как я понимаю, подспорьем при ее создании был фундаментальный труд Кента Питмана, обобщающий опыт в этой сфере, &lt;a href="http://www.nhplace.com/kent/Papers/Large-Systems.html"&gt;"Описание больших систем"&lt;/a&gt; (1984 года), а также опыт эксплуатации ее предшественника MK:DEFSYSTEM.  Т.е., по сути, ASDF была "второй системой", но в данном случае, к счастью, удалось избежать реализации соответствующего синдрома.&lt;br /&gt;&lt;br /&gt;Что же представляет из себя эта сама по себе довольно большая система изнутри?  Хребтом ASDF является иерархия классов &lt;code&gt;component&lt;/code&gt; → &lt;code&gt;module&lt;/code&gt; → &lt;code&gt;system&lt;/code&gt;, которые содержат информацию об именах, местоположении и зависимостях систем и их компонент, метаинформацию, а также служебную информацию самой ASDF.&lt;br /&gt;&lt;br /&gt;Кроме того описаны классы операций, которые могут выполняться над системами, как то: &lt;code&gt;compile-op&lt;/code&gt;, &lt;code&gt;load-op&lt;/code&gt;, &lt;code&gt;test-op&lt;/code&gt; и т.д.  Почему операции являются объектами, а не просто ключами, что напрашивается на первый взгляд?  Во-первых, это позволяет наследовать от них и при этом точечно менять поведение связанных обобщенных функций.  Но даже более важно то, что объект операции имеет определенные свойства: операцию-родитель, является ли операция форсированной (хотя это свойство на данный момент не работает корректно), таблицы отработанных и еще не отработанных узлов и т.д.  Это имеет и свой недостаток, поскольку кажется несколько избыточным для обычного пользователя.  В версии ASDF 2 для его устранения введены функции-обертки &lt;code&gt;load-system&lt;/code&gt;, &lt;code&gt;compile-system&lt;/code&gt; и &lt;code&gt;test-system&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;На уровне пользовательского API на основе всех этих классов функционирует макро &lt;code&gt;defsystem&lt;/code&gt;, а также обобщенная функция &lt;code&gt;operate&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Defsystem&lt;/code&gt; — это хороший инструмент описания систем, знакомый и понятный, я думаю, каждому.  Он ведет свою историю еще от Lisp Machine Lisp &lt;code&gt;DEFSYSTEM&lt;/code&gt;, хотя с тех пор и существенно эволюционировал в сторону упрощения интерфейса.&lt;br /&gt;&lt;br /&gt;Параметры &lt;code&gt;defsystem&lt;/code&gt;-формы не передаются, как можно было бы предположить, напрямую в &lt;code&gt;(make-instance 'system ...)&lt;/code&gt;, а сперва обрабатываются функцией &lt;code&gt;parse-component-form&lt;/code&gt;.  При этом часть параметров передается как есть, а часть транслируется или используется в качестве мета-параметров.  Остановлюсь на двух из них:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;defsystem&lt;/code&gt; можно применять не только для стандартных систем, но и их потомков за счет параметра &lt;code&gt;:class&lt;/code&gt;&lt;/li&gt;&lt;li&gt;параметр &lt;code&gt;:depends-on&lt;/code&gt; (и &lt;code&gt;:weakly-depends-on&lt;/code&gt;&lt;a href="http://www.blogger.com/post-create.g?blogID=6031647961506005424#foot1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;), на самом деле, не присутствует в качестве слота в классе &lt;code&gt;component&lt;/code&gt;.  Его содержимое транслируется в содержимое слота &lt;code&gt;in-order-to&lt;/code&gt;, которое описывает зависимости более гранулярно отдельно для каждой операции.  Кстати, этот слот можно задать напрямую в &lt;code&gt;defsystem&lt;/code&gt;-описании, чем иногда пользуются при необходимости указания нестандартных сценариев поведения.  Впрочем, среди установленных у меня порядка 70 библиотек я нашел только несколько примеров такого использования, самый интересный из которых — в описании системы weblocks-prevalance (в остальных случаях это применяется для указания зависимостей систем-тестовых наборов).  В данном случае устанавливается зависимость от дополнительно определенной операции &lt;code&gt;prepare-prevalance-op&lt;/code&gt;:&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;(defsystem weblocks-prevalence&lt;br /&gt;   :name "weblocks-prevalence"&lt;br /&gt;   ;; кстати, нет смысла задавать параметр `name' для системы,&lt;br /&gt;   ;; поскольку имя берется из символа, передаваемого в &lt;code&gt;defsystem&lt;/code&gt;&lt;br /&gt;   :description "A weblocks backend for cl-prevalence."&lt;br /&gt;   :depends-on (:metatilities :cl-ppcre :cl-prevalence :bordeaux-threads)&lt;br /&gt;   :components ((:file "prevalence"))&lt;br /&gt;   :in-order-to ((compile-op (prepare-prevalence-op :weblocks-prevalence))&lt;br /&gt;                 (load-op (prepare-prevalence-op :weblocks-prevalence))))&lt;/code&gt;&lt;/pre&gt;А сама операция &lt;code&gt;prepare-prevalence-op&lt;/code&gt; характеризуется всего одним дополнительным методом, отвечающим за подгрузку дополнительной системы, находящейся по внутреннему пути, не известному в &lt;code&gt;*central-registry*&lt;/code&gt;:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;(defmethod perform ((op prepare-prevalence-op)&lt;br /&gt;                    (c (eql (find-system :weblocks-prevalence))))&lt;br /&gt;  (unless (find-package :weblocks-memory)&lt;br /&gt;  ;; load weblocks if necessary&lt;br /&gt;  (unless (find-package :weblocks)&lt;br /&gt;    (asdf:oos 'asdf:load-op :weblocks))&lt;br /&gt;  ;; load weblocks-memory.asd&lt;br /&gt;  (load (merge-pathnames&lt;br /&gt;         (make-pathname :directory '(:relative "src" "store" "memory")&lt;br /&gt;                        :name "weblocks-memory" :type "asd")&lt;br /&gt;         (funcall (symbol-function&lt;br /&gt;                   (find-symbol (symbol-name '#:asdf-system-directory)&lt;br /&gt;                                (find-package :weblocks)))&lt;br /&gt;                  :weblocks)))&lt;br /&gt;  ;; load weblocks-memory&lt;br /&gt;  (asdf:oos 'asdf:load-op :weblocks-memory)))&lt;/code&gt;&lt;/pre&gt;Впрочем, это можно было бы сделать и иначе :)&lt;br /&gt;&lt;br /&gt;Проблемой использования &lt;code&gt;defsystem&lt;/code&gt;, которой я коснусь в следующей статье на тему шаблонов применения ASDF, является то, что есть соблазн отступить о чисто декларативного описания системы и добавить в него некоторые исполняемые элементы, например, чтение и подстановку версии системы из отдельного файла.  Как по мне, было бы разумно обрабатывать эту форму в рамках &lt;code&gt;(let ((*read-evel* nil) ...)&lt;/code&gt;, чтобы исключить такие варианты.  Причина тут в том, что ASDF-описание может обрабатыватся более, чем 1 раз при поиске систем и разрешении зависимостей, и работа с ним в таком случае выполняется в режиме просто чтения.  Возможно, это ограничение будет со временем установлено: во всяком случае это уже обсуждалось в рассылке.&lt;br /&gt;&lt;br /&gt;Теперь рассмотрим функцию &lt;code&gt;operate&lt;/code&gt;, которая является точкой входа в область собственно ядра ASDF, которое отвечает за поиск и выполнение операций над зависимыми компонентами.  Она опирается на обобщенные функции &lt;code&gt;traverse&lt;/code&gt;, роль которой — в построении плана выполнения той или иной операции, и &lt;code&gt;perform&lt;/code&gt;, которая собственно выполняет конечные действия, будь то компиляция, загрузка файлов и т.д, а также на функцию &lt;code&gt;find-system&lt;/code&gt;.  Кроме того, интересным дополнением (неким альтер-его) &lt;code&gt;perform&lt;/code&gt; является &lt;code&gt;explain&lt;/code&gt;, которая только указывает, какое действие должно быть выполнено.  Хорошая иллюстрация возможностей применения &lt;code&gt;explain&lt;/code&gt; дана в статье Питмана:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(DOLIST (STEP (SYSDEF:GENERATE-PLAN system :UPDATE))&lt;br /&gt;  (SYSDEF:EXPLAIN-ACTION system STEP)&lt;br /&gt;  (UNLESS (NOT (Y-OR-N-P "OK? "))&lt;br /&gt;     (SYSDEF:EXECUTE-ACTION system STEP)))&lt;/code&gt;&lt;/pre&gt;Этот код на Lisp Machine Lisp позволяет пошагово выполнять обновление системы при условии согласия пользователя на каждом шаге.&lt;br /&gt;&lt;br /&gt;Операция &lt;code&gt;traverse&lt;/code&gt; реализует алгоритм поиска и разрешения зависимостей ASDF.  Сам по себе он не стоит отдельного рассмотрения, но что интересно, это то, что алгоритм может обрабатывать 3 типа зависимостей:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;привычную простую форму (&lt;code&gt;:depends-on (:cl-ppcre ...)&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;версионированную форму (&lt;code&gt;:depends-on ((:version :cl-ppcre "1.2.3") ...)&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;зависимость от фичи (&lt;code&gt;:depends-on ((:feature :x86) ...)&lt;/code&gt;)&lt;/li&gt;&lt;/ul&gt;Ко второй форме мы еще вернемся в теме о поддержке версионности.  А на счет третьей, то к ней относится интересный комментарий в коде ASDF: "Congratulations, you're the first ever user of FEATURE dependencies! Please contact the asdf-devel mailing-list." :)&lt;br /&gt;&lt;br /&gt;Точкой входа в механизм поиска Лисп-систем в операционной системе является функция &lt;code&gt;find-system&lt;/code&gt;, которая смоделированна на основе стандартной функции &lt;code&gt;find-class&lt;/code&gt;.  (Артефактом такого подобия является параметр &lt;code&gt;errorp&lt;/code&gt;, необходимость в котором как здесь, так и в &lt;code&gt;find-class&lt;/code&gt; и &lt;code&gt;find-method&lt;/code&gt;, в которых также реализован этот подход, как по мне, по меньшей мере сомнительна).  &lt;code&gt;Find-system&lt;/code&gt; одновременно проверяет наличие системы среди уже загруженных (таблица таких систем со врменем последнего обновления находится в &lt;code&gt;*defined-systems*&lt;/code&gt;), а также ищет на диске с помощью функции &lt;code&gt;system-definition-pathname&lt;/code&gt;, которая в свою очередь раскручивает механиз поиска систем функциями, заданными в списке &lt;code&gt;*system-definition-search-functions*&lt;/code&gt;.  На данный момент в этом списке 2 основных функции: "классический" поиск в директориях, заданных в &lt;code&gt;*central-registry*&lt;/code&gt;, и новый поиск в &lt;code&gt;source-registry&lt;/code&gt;.  Очень важной особенностью &lt;code&gt;find-system&lt;/code&gt;, про которую не нужно забывать, это ее побочный эффект — загрузка ASD-файла системы в память и регистрация его в &lt;code&gt;*defined-systems*&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Наконец, стоит еще упомянуть 2 обобщенные функции &lt;code&gt;output-files&lt;/code&gt; и &lt;code&gt;input-files&lt;/code&gt;, которые позволяют задавать способ точного определения полных имен файлов разных типов компонент по их короткому имени в описании системы.&lt;br /&gt;&lt;br /&gt;В общем-то, это и всё ядро ASDF.  Остальное — это ряд утилит для работы с операционной системой, среди которых несколько очень полезных функций, заслуживающих более широкой известности в Лисп-мире (например, &lt;code&gt;run-shell-command&lt;/code&gt;, &lt;code&gt;load-pathname&lt;/code&gt;, &lt;code&gt;parse-windows-shortcut&lt;/code&gt; и другие), а также добавленные в ASDF 2 механизмы определения местонахождения FASL-файлов (в чем-то аналог ASDF-BINARY-LOCATIONS, для которого добавлен и compatibily mode) и работы с центральным реестром.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;Разобравшись во внутренностях ASDF я пришел к довольно неожижанному для себя выводу: в ее основе лежит хорошо продуманный объектно-ориентированный дизайн, дающий воможность для ее эффективного применения не только непосредственно, но и как основы для других инструментов.  Более того, используя ее как кейс, можно даже учить людей настоящему практическому объектно-ориентированному проектированию&lt;a href="http://www.blogger.com/post-create.g?blogID=6031647961506005424#foot2"&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;.  В то же время, этот дизайн, конечно, не идеален.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Во-первых, он сложен.  И это действительно оправдано причинами, описанными вначале.  Но все же временами наблюдается излишняя сложность.  (Впрочем, эта проблема постепенно устраняется по мере развития кода).  Сложность приводит к багам, некоторые из которых существуют до сих пор, на 10-м году жизни системы.&lt;/li&gt;&lt;li&gt;Во-вторых, он расширяемый, но тоже не до конца: ядро системы создано с оглядкой на последующую расширяемость, но в поддерживающем слое об этом иногда забывали.&lt;/li&gt;&lt;li&gt;В-третьих, он плохо описан.  И это, пожалуй, самая большая проблема ASDF и хороший урок для любого проектировщика: ясная и полная документация имеет важнейшее значение для удачного использования сколь угодно хорошого дизайна.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;И еще можно сказать, что ASDF намного ближе по своей философии к (качественным) продуктам "классических" системных языков, таких как С++ или Java, чем к распространенному в последнее время в Лиспе bottom-up стилю.  В то же время, за счет использования полезных возможностей Лиспа: мультиметодов, функций высших порядков и т.п.,— он намного менее церемониален и многословен, так сказать, без перегрузки шаблонами проектирования.&lt;br /&gt;&lt;br /&gt;В следующей статье — о некоторых шаблонах использования ASDF.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;a name="foot1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; разница в том, что изменение "слабых" зависимостей не вызывает перекомпиляцию зависящих от них компонент&lt;br /&gt;&lt;a name="foot1"&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; ... а не такому далекому на поверку от реальности, который можно увидеть, например, в знаменитой книге Гради Буча&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;И в придачу, непонятная визуализация того, как происходит поиск системы:)&lt;br /&gt;&lt;img src="http://img.photobucket.com/albums/v473/pufpuf/sysdef-search.jpg" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-7566368012936801455?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/7566368012936801455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=7566368012936801455' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/7566368012936801455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/7566368012936801455'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/07/asdf.html' title='Внутреннее устройство ASDF'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-6852166418451880367</id><published>2010-07-05T08:43:00.005+03:00</published><updated>2010-07-05T08:55:33.664+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Уникальные технологии Common Lisp</title><content type='html'>&lt;span id="for-and-date"&gt;&lt;strong&gt;Написано для:&lt;/strong&gt; &lt;a href="http://www.developers.org.ua/archives/vseloved/2008/10/22/common-lisp-technologies/"&gt;developers.org.ua&lt;/a&gt;&lt;br /&gt;&lt;strong&gt;Время написания:&lt;/strong&gt; октябрь 2008&lt;/span&gt;&lt;br /&gt;&lt;h3&gt;Базовые подсистемы языка&lt;/h3&gt;&lt;p&gt;В языке Common Lisp есть как минимум 3 инфраструктурных технологии, во многом формирующие подходы к его применению, которые в других языках либо отсутствуют вовсе, либо реализованы в очень ограниченном варианте. Для компенсации их отсутствия пользователи других языков часто вынуждены использовать Шаблоны проектирования, а порой и вообще не имеют возможности применять некоторые более эффективные подходы к решению типичных задач.&lt;/p&gt;&lt;p&gt;Что это за технологии и какие возможности дает их использование?&lt;/p&gt;&lt;h4&gt;Макросистема&lt;br /&gt;&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;Это основная отличительная особенность Common Lisp, выделяющая его среди других языков. Ее реализация возможна благодаря использованию для записи Lisp-програм &lt;a href="http://en.wikipedia.org/wiki/S-expression"&gt;s-нотации&lt;/a&gt; (представления программы непосредственно в виде ее абстрактного синтаксического дерева). Позволяет программировать компилятор языка.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Позволяет полностью соблюдать один из основополагающих принципов хорошего стиля программирования DRY (не-повторяй-себя).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;В отличие от обычных функций, аргументы, передаваемые макросам, не вычисляются, поэтому с их помощью можно создавать любые управляющие конструкции языка.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;Примеры применения:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Определение управляющих конструкций языка, которые могут использоваться на равне со стандартными (на самом деле практически все стандартные управляющие конструкции также являются макросами. Основу языка — “аксиомы”, которые невозможно определить через другие конструкции — составляют &lt;a href="http://gigamonkeys.com/book/the-special-operators.html"&gt;специальные операторы&lt;/a&gt;). В качестве примера можно привести &lt;a href="http://en.wikipedia.org/wiki/Anaphora_%28linguistics%29"&gt;анафорические&lt;/a&gt; управляющие конструкции (см. библиотеку &lt;a href="http://common-lisp.net/project/anaphora/"&gt;Anaphora&lt;/a&gt;), которые, используя принцип “convention over configuration”, скрывают реализацию некоторых типичных шаблонов.&lt;br /&gt;&lt;p&gt;Самый простой пример — макро AIF (или IF-IT), которое тестирует первый аргумент на истинность и одновременно привязывает его значение к переменной IT, которую, соответственно, можно использовать в THEN-clause:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;(defmacro aif (var then &amp;amp;optional else)&lt;br /&gt;`(let ((it ,var))&lt;br /&gt;  (if it ,then ,else)))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Учитывая то, что в CL ложность представляется константой NIL, которая также соответствует пустому списку, такая конструкция, например, часто применяется в коде, где сначала какие-то данные аккумулируются в список, а потом, если список не пуст, над ними производятся какие-то действия. Другой вариант, это проверить, заданно ли какое-то значение и потом использовать его:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;(defun determine-fit-xture-type (table-str)&lt;br /&gt;"Determine a type of Fit fixture, specified with TABLE-STR"&lt;br /&gt;(handler-case&lt;br /&gt;    (aif (find (string-trim *spacers* (strip-tags (get-tag "td" (get-tag "tr" table-str 0) 1)))&lt;br /&gt;               *fit-xture-rules* :test #'string-equal :key #'car)&lt;br /&gt;         (cdr it)&lt;br /&gt;         'row-fit-xture)&lt;br /&gt;  (tag-not-found () 'column-fit-xture)))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;small&gt;* В этой функции проверяется, есть ли во второй ячейке первой строки HTML таблицы какие-то данные и в соответствии с этим определяется тип привязки для Fit-теста. Переменной it присвоены найденные данные.&lt;/small&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;Создание &lt;a href="http://en.wikipedia.org/wiki/Domain-specific_programming_language"&gt;DSL&lt;/a&gt;‘ей для любой предметной области, которые могут иметь в распоряжении все возможности компилятора Common Lisp. Ярким примером такого DSL’я может служить библиотека &lt;a href="http://common-lisp.net/project/parenscript/"&gt;Parenscript&lt;/a&gt;, которая реализует кодогенерацию JavaScript из Common Lisp. Используя ее, можно писать макросы для Javascript!&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(js:defpsmacro set-attr (id attr val)&lt;br /&gt;`(.attr ($ (+ "#" ,id)) ,attr ,val))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;small&gt;* Простейший макрос-обертка для задания аттрибутов объекта, полученного с помощью селектора jQuery&lt;/small&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;В форме локальных макросов (MACROLET) для модуляризации и разделения потоков вычислений внутри сложных функций, а также для соблюдения принципа DRY при написании лишь слегка отличающегося кода в различных местах одной функции.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Наконец, создание инфраструктурных систем языка. Например, с помощью макросов можно реализовать продления (библиотека &lt;a href="http://common-lisp.net/project/cl-cont/"&gt;CL-CONT&lt;/a&gt;), ленивые вычисления (библиотека &lt;a href="http://series.sourceforge.net/"&gt;SERIES&lt;/a&gt;) и т.д.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;…ну и для многих других целей.&lt;/li&gt;&lt;/ol&gt;Больше по теме: &lt;a href="http://paulgraham.com/onlisp.html"&gt;Paul Graham, &lt;em&gt;On Lisp&lt;/em&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Мета-объектный протокол и CLOS&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;Основа объектной системы языка. Позволяет манипулировать представлением классов.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Методы не принадлежат классам, а специализируются на них, что дает возможность элегантной реализации множественной диспетчиризации. Также возможна специализация не по классу, а по ключу.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Уникальной является технология комбинации методов, позволяющая использовать стандартные способы комбинации: перед, после, вокруг,— а также определенные пользователем.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Примерами использования мета-объектного протокола также являются инфраструктурные системы языка, реализованные в виде библиотек:&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;object-persisance: Elephant, AllegroCache&lt;/li&gt;&lt;li&gt;работа с БД: CLSQL&lt;/li&gt;&lt;li&gt;интерфейс пользователя: Cells&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Библиотека &lt;a href="http://clsql.b9.com/"&gt;CLSQL&lt;/a&gt; создана для унификации работы с различными SQL базами данных. Кстати, на ее примере можно увидеть проявление мультипарадигменности Common Lisp: у библиотеки есть как объектно-ориентированный интерфейс (ORM), реализованный на основе CLOS, так и функциональный (на основе функций и макросов чтения).&lt;/p&gt;&lt;p&gt;С помощью мета-объектного протокола стандартный класс языка расширяется специальным параметром — ссылкой на таблицу БД, к которой он привязан, а описания его полей (в терминологии Lisp: слотов) — дополнительными опциональными параметрами, такими как: ограничение уникальности, ключа, функция-преобразователь при записи и извлечении значения из БД и т.д.&lt;/p&gt;Больше по теме: &lt;a href="http://books.google.com/books?id=3X5Gnudn3k0C"&gt;Gregor Kiczales et al. &lt;em&gt;The Art of Metaobject Protocol&lt;/em&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Система обработки ошибок / сигнальный протокол&lt;/h4&gt;&lt;p&gt;Система обработки ошибок есть в любом современном языке, однако в CL она все еще остается в определенном смысле уникальной (разве что в C# сейчас вводится нечто подобное). Преимущество этой системы заключается опять же в ее большей абстрактности: хотя основная ее задача — обработка ошибок, точнее исключительных ситуаций,— она построена на более общей концепции передачи управления потоком выполнения программы по стеку. …Как и системы в других языках. Но в других языках есть единственный предопределенный вариант передачи управления: после возникновения исключительной ситуации стек отматывается вплоть до уровня, где находится ее обработчик (или до верхнего уровня). В CL же стек не отматывается сразу, а сперва ищется соответствующий обработчик (причем это может делаться как в динамическом, так и в лексическом окружении), а затем обработчик выполняется на том уровне, где это определенно программистом. Таким образом, исключительные ситуации не несут безусловно катастрофических последствий для текущего состояния выполнения программы, т.е. с их помощью можно реализовать различные виды нелокальной передачи управления (а это приводит к сопроцедурам и т.п.) Хорошие примеры использования сигнального протокола приведены в книге Practical Common Lisp (см. ниже).&lt;/p&gt;Больше по теме:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.nhplace.com/kent/Papers/Condition-Handling-2001.html"&gt;Kent Pitman, &lt;em&gt;Condition Handling in the Lisp Language Family&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html"&gt;Peter Siebel, &lt;em&gt;Practical Common Lisp, Ch.19 “Beyond Exception Handling: Conditions and Restarts”&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h3&gt;Вспомогательные технологии&lt;/h3&gt;&lt;p&gt;Кроме того в CL есть ряд технологий менее значительных, которые нельзя назвать в полной мере уникальными, но которые существенно упрощают его применение и делают программы более ясными, а также дают дополнительные возможности для расширения языка:&lt;/p&gt;&lt;h4&gt;Протокол множественных возвращаемых значений&lt;/h4&gt;&lt;p&gt;Дает возможность возвращать из функции несколько значений и по желанию принимать все их (и привязывать к каким-то переменным) или только часть. По-умолчанию для кода, не использующего эту функциональность, передается только 1-е значение.&lt;/p&gt;&lt;p&gt;Казалось бы, это простая возможность, однако, на поверку, она требует обширной поддержки на языковом уровне (учитывая необходимость поддержки возврата из блоков и т.п.).&lt;/p&gt;&lt;h4&gt;Протокол обобщенных переменных&lt;/h4&gt;&lt;p&gt;Это аналог свойств в некоторых ОО-языках. Концептуально, оперирует понятием места (place) — по сути дела ячейки памяти, однако не физической (без манипуляции указателями) — это может быть просто объект или же элемент какой-то структуры (будь-то опять же объект, список, массив и т.д.) Таким образом, имеются намного большие возможности, чем при использовании обычных свойств, поскольку для любой функции, которая читает значения какого-либо места, можно указать функцию которая его значение задает.&lt;/p&gt;Больше по теме: &lt;a href="http://www.bookshelf.jp/texi/onlisp/onlisp_13.html"&gt;Paul Graham, &lt;em&gt;On Lisp, Ch.12 “Generalized Variables”&lt;/em&gt;&lt;/a&gt;&lt;br /&gt;&lt;h4&gt;Макросы чтения&lt;/h4&gt;&lt;p&gt;Это инструмент модификации синтаксиса языка за пределы s-выражений, который дает программисту возможность, используя компилятор Lisp, создать свой собственный синтаксис. Его работа основана на фундаментальном принципе Lisp-систем: разделении времени чтения, времени компиляции и времени выполнения — REPL (Read-Eval-Print Loop). Обычные макросы вычисляются (раскрываются, expand) во время компиляции, и полученный код компилируется вместе с написанным вручную. А вот макросы чтения выполняются еще на этапе обработки программы парсером при обнаружении специальных символов (dispatch characters). Механизм макросов чтения является возможностью получить прямой доступ к Reader’у и влиять на то, как он формирует абстрактное синтаксическое дерево из “сырого” программного кода. Таким образом, можно на поверхности Lisp использовать любой синтаксис, вплоть до, например, C-подобного. Впрочем, Lisp-программисты предпочитают все-таки префиксный унифицированный синтаксис со скобками, а Reader-макросы используют для специфических задач.&lt;/p&gt;&lt;p&gt;Пример такого использования — буквальный синтаксис для чтения hash-таблиц, который почему-то отсутствует в спецификации языка. Это, кстати, еще один пример того, каким образом CL дает возможность изменить себя и использовать новые базовые синтаксические конструкции наравне с определенными в стандарте. Основывается на буквальном синтаксисе для ассоциативных списков (ALIST):&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="lisp"&gt;;; a reader syntax for hash tables like alists: #h([:test (test 'eql)] (key . val)*)&lt;br /&gt;(set-dispatch-macro-character #\# #\h&lt;br /&gt;   (lambda (stream subchar arg)&lt;br /&gt;     (declare (ignore subchar)&lt;br /&gt;              (ignore arg))&lt;br /&gt;     (let* ((sexp (read stream t nil t))&lt;br /&gt;            (test (when (eql (car sexp) :test) (cadr sexp)))&lt;br /&gt;            (kv-pairs (if test (cddr sexp) sexp))&lt;br /&gt;            (table (gensym)))&lt;br /&gt;       `(let ((,table (make-hash-table :test (or ,test 'eql))))&lt;br /&gt;          (mapcar #'(lambda (cons)&lt;br /&gt;                      (setf (gethash (car cons) ,table)&lt;br /&gt;                            (cdr cons)))&lt;br /&gt;                  ',kv-pairs)&lt;br /&gt;          ,table)))))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Больше по теме: &lt;a href="http://letoverlambda.com/textmode.cl/guest/chap4.html"&gt;Doug Hoyte, &lt;em&gt;Let Over Lambda, Ch.4 “Read Macros”&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://letoverlambda.com/textmode.cl/guest/chap4.html"&gt;&lt;em&gt;&lt;br /&gt;&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;Послесловие&lt;/h3&gt;&lt;p&gt;В заключение хотелось бы коснуться понятия &lt;strong&gt;высокоуровневого языка программирования&lt;/strong&gt;. Оно, конечно, является философским, поэтому выскажу свое мнение на этот счет: по-настоящему высокоуровневый язык должен давать программисту возможность выражать свои мысли, концепции и модели в программном коде напрямую, а не через другие концепции, если только те не являются более общими. Это значит, например, что высокоуровневый язык должен позволять напрямую оперировать такой сущностью, как функция, а не требовать для этого задействовать другие сущности такого же уровня абстракции, скажем, классы. Подход к созданию высокоуровневого языка можно увидеть на примере Common Lisp, в котором для каждой задачи выбирается подходящая концепция, будь то объект, сигнал или место. А что дает нам использование по-настоящему высокоуровневых языков? Большую расширяемость, краткость и адаптируемость программы к изменениям, и, в конце концов, настоящую свободу при программировании!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-6852166418451880367?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/6852166418451880367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=6852166418451880367' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6852166418451880367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6852166418451880367'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/07/common-lisp.html' title='Уникальные технологии Common Lisp'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-8843827580784843147</id><published>2010-07-05T08:26:00.003+03:00</published><updated>2010-07-05T08:34:47.919+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Интересная задачка: вытесняющий мультипроцессинг в userland</title><content type='html'>Нужно в рамках одной нити управления реализовать поочередно работающие 2 "процесса" (скажем, вычисление 2-х функций), переключение между которыми происходит по регулярному сигналу таймера.  Естественно, что при переключении состояние вычисления должно сохраняться и восстанавливаться на следующем такте (а не начинаться заново каждый раз).&lt;br /&gt;&lt;br /&gt;Интересно было бы увидеть, как это реализовывается в разных языках?  (Я так понимаю, что в Smalltalk это должно быть тривиально за счет наличия объекта контекста.  А где еще?)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-8843827580784843147?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/8843827580784843147/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=8843827580784843147' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8843827580784843147'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8843827580784843147'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/07/userland.html' title='Интересная задачка: вытесняющий мультипроцессинг в userland'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-8118609682143392193</id><published>2010-06-30T12:56:00.003+03:00</published><updated>2010-06-30T13:05:37.115+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>ASDF 2</title><content type='html'>Собираюсь написать серию постов про ASDF: его текущее развитие в связи с выходом ASDF 2, внутреннее устройство, шаблоны и некоторые идеи по поводу его использования.  Вообще, в последние пару лет в Lisp-сообществе к этой теме (не только собственно ASDF, но и в целом управление сборкой и дистрибутивами), очень живой интерес, поскольку есть вопросы, которые требуют решения.  Недавняя статья, дающая пищу для размышлений: &lt;a href="http://tream.dreamhosters.com/tream/musings/49-lisp/76-analysis-of-existing-asdf-files"&gt;Анализ использования ASDF в разных проектах&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;ASDF является фактически единственным на данный момент сборщиком Common Lisp програм.  Более того он играет свою роль в различных менеджерах дистрибутивов, хотя и не является самим по себе полноценным решением.  И, я бы сказал, что со своей ролью инструмента сборки он справляется довольно неплохо, а вот в сфере описания дистрибутивов есть проблемы, которые пока что не решены на практике.  Это заставляет многих людей, в том числе и меня, вообще не пользоваться подобными инструментами и произвоить установку дистрибутивов вручную (благо в Common Lisp среде, в том числе и благоаря ASDF это очень просто&lt;sup&gt;1&lt;/sup&gt;), а других (в том числе и меня) задумыватся о создании собственного средства: примеры тому — Mudballs, Lispy, CL-Librarian, LibCL...&lt;br /&gt;&lt;br /&gt;В &lt;a href="http://fprog.ru/2010/issue5/vsevolod-dyomkin-lisp-philosophy/"&gt;недавней статье в журнале ПФП&lt;/a&gt; я написал, что&lt;br /&gt;&lt;blockquote&gt;развитие средства управления пакетами должно идти именно с учетом децентрализованной структуры Лисп-среды, а не вопреки ей.&lt;/blockquote&gt;&lt;br /&gt;К такому же заключению мы пришли и в процессе &lt;a href="http://lisper.ru/forum/thread/343"&gt;обсуждения темы на форуме lisper.ru&lt;/a&gt;, которое также заставило меня немного заглянуть под капот ASDF, чтобы узнать, насколько реально и легко создать на его основе пакетный менеджер.  Этот "быстрый взгляд" в итоге вылился в растянутое на целый месяц неспешное ковыряние кода и его доработку для полноценной поддержки версионирования.  Свое решение я направил в список рассылки &lt;a href="http://common-lisp.net/pipermail/asdf-devel/2010-June/001514.html"&gt;asdf-devel&lt;/a&gt;, однако оно вряд ли будет интегрированно.  В любом случае, этой теме я собираюсь посвятить отдельную запись в этой серии.&lt;br /&gt;&lt;br /&gt;А начнем мы с того, что нового нам несет ASDF 2, релиз которого состоялся 31 мая, и который уже скоро войдет в вашу любимую Лисп реализацию.&lt;br /&gt;&lt;br /&gt;Целая новая версия системы претерпела существенный рефакторинг и доработку силами Фарэ и Роберта Голдмана.  С точки зрения пользователей она включает в себя следующие улучшения (разумеется, накопленные и отлаженные за посление ряд релизов):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;версионность самой библиотеки, возможность обновления ASDF&lt;/li&gt;&lt;br /&gt;&lt;li&gt;добавление более простого пользовательского интерфейса&lt;/li&gt;&lt;br /&gt;&lt;li&gt;добавление нового способа конфигурации&lt;/li&gt;&lt;br /&gt;&lt;li&gt;сохранение FASL-файлов отдельно (в других директориях) от исходного кода&lt;/li&gt;&lt;br /&gt;&lt;li&gt;улучшение работы c путями и соответствующее расширение классов &lt;code&gt;component&lt;/code&gt; и &lt;code&gt;system&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;исправление некоторых багов&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Вот &lt;a href="http://www.blogger.com/vimeo.com/12072117"&gt;lightning talk Роберта на встрече TwinCity лисперов&lt;/a&gt;, посвященный ASDF 2.  Нужно заметить, что ценность этого релиза не только практическая, но и символическая, знаменующая переход проекта на новую стадию разработки: более структурированную и прогнозируемую.&lt;br /&gt;&lt;br /&gt;Итак, кратко о новых фичах.&lt;br /&gt;&lt;br /&gt;1. Возможность обновления ASDF.&lt;br /&gt;Сейчас любой пользователь может воспользоваться самой новой версией ASDF, просто загрузив ее через &lt;code&gt;(asdf:oos 'asdf:load-op 'asdf)&lt;/code&gt; (но не через &lt;code&gt;require&lt;/code&gt;!)  Подробнее об этом в &lt;a href="http://common-lisp.net/project/asdf/asdf.html"&gt;мануале&lt;/a&gt;, который, также улучшается.&lt;br /&gt;&lt;br /&gt;2. Более простой пользовательский интерфейс — это функции &lt;code&gt;load-system&lt;/code&gt;, &lt;code&gt;compile-system&lt;/code&gt; и &lt;code&gt;test-system&lt;/code&gt;.  Вроде бы как, тривиальное изменение, но избавляющее новичков от необхоимости думать, почему это операции &lt;code&gt;load-op&lt;/code&gt; и т.п. являются объектами, а не функциями, и других схожих волнений.  Это важно в борьбе с &lt;b&gt;кажущейся&lt;/b&gt; сложностью ASDF.  При этом методы на &lt;code&gt;test-op&lt;/code&gt;, как и раньше — и это понятно — нужно описывать разработчикам систем.&lt;br /&gt;&lt;br /&gt;3. Системный реестр (&lt;code&gt;source-registry&lt;/code&gt;) — новый способ конфигурации :)&lt;br /&gt;Именно таким является новый, и по замыслу авторов основной способ задания места расположения исходников систем у пользователя.  В то же время старый вариант через &lt;code&gt;*central-registry*&lt;/code&gt; остается полностью функциональным и поддерживаемым.  Более того, он был дополнен проверкой на самую неприятную и, наверно, частую ошибку при использовании ASDF — отсутствие слеша в конце пути к директориям — теперь этой неразберихи больше не будет.&lt;br /&gt;&lt;br /&gt;Что же такое системный реестр?  Это набор конфигурационных файлов в предопределенной структуре директорий для каждого пользователя, смоделированных по принципу *.conf.d директорий в Unix.  А также собственно DSL для конфигурации.  Простой пример того, как это работает из мануала:&lt;br /&gt;&lt;blockquote&gt;В директории &lt;code&gt;~/.config/common-lisp/&lt;/code&gt; находится файл &lt;code&gt;source-registry.conf&lt;/code&gt; со следующей конфигурацией:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;(:source-registry&lt;br /&gt; (:tree "/home/fare/cl/")&lt;br /&gt; :inherit-configuration)&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;В данном случае поиск установленных систем производится рекурсивно в поддиректориях в &lt;code&gt;/home/fare/cl/&lt;/code&gt;.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Однако это объяснение и пример далеко не исчерпывающи, поэтому лучше читать &lt;a href="http://common-lisp.net/project/asdf/asdf.html#Controlling-where-ASDF-searches-for-systems"&gt;соответствующий раздел руководства&lt;/a&gt; и проверить все собственноручно.&lt;br /&gt;&lt;br /&gt;Честно говоря, как по мне, то новый подход для индивидуального разработчика менее удобен, чем использование &lt;code&gt;*central-registry*&lt;/code&gt;.  Однако он лучше подойдет для средств автоматического конфигурирования (и, я думаю, что как раз опыт в рамках ITA, где используется много Lisp серверов приложений, послужил отправной точкой для разработки этого способа), а также для использования в пакетных менеджерах.  И хорошо, что теперь есть альтернативы для каждого из случаев.&lt;br /&gt;&lt;br /&gt;4. Cохранение FASL-файлов теперь происходит в компилятор-специфичных директориях, по умолчанию спрятанных в домашней диретории пользователя.  Благодаря этому устраняются проблемы как конфликта прав в случае использования одних и тех же исходников библиотек разными пользователями, так и stale FASLs, которые возникают при апгрейде реализации (в частности для SBCL).&lt;br /&gt;&lt;br /&gt;5. В общем, исправлены основные недочеты, которые присутствовали в ASDF при работе с путями в разных операционках, а в классы компонент и система добавлены слоты, указывающие абсолютное положение их в файловой системе.  Также исправлены и некоторые другие баги, о чем можно почитать в &lt;a href="http://common-lisp.net/gitweb?p=projects/asdf/asdf.git;a=log"&gt;Changelog'е&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Какие проблемы остались?  Из существенных для меня — две: нечеткая семантика форсированных операций (параметр &lt;code&gt;:force t&lt;/code&gt;), а также недостатки работы с версиями (этой теме будет посвящена отдельная запись, поэтому не буду касаться ее здесь).&lt;br /&gt;&lt;br /&gt;В завершение скажу, что, очевидно, ASDF будет развиваться, как минимум, в сторону устранения явных проблем и упрощения работы с ним, а также, возможно, и поддержки большего количества сценариев использования и перехода к еще более декларативной модели описания систем.&lt;br /&gt;&lt;br /&gt;В следующей записи — немного о внутренностях ASDF...&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;div style="font-size: smaller;"&gt;&lt;br /&gt;&lt;sup&gt;1&lt;/sup&gt; Мой алгоритм установки Lisp библиотеки:&lt;br /&gt; - Загрузить tar.gz файл&lt;br /&gt; - Развернуть в &lt;code&gt;~/lib/lisp/&lt;/code&gt;&lt;br /&gt; - Создать символическую ссылку на ASD-файл в &lt;code&gt;~/.lisp/&lt;/code&gt;&lt;br /&gt; - (И вариация для случая работы с разными версиями одного пакета): ссылка на ASD-файл основной версии в &lt;code&gt;~/.lisp/&lt;/code&gt;, а при необходимости использования альтернативной версии, скажем hunchentoot-0.15.7 вместо hunchentoot-1.1.0 &lt;code&gt;(push "~/lib/lisp/hunchentoot-0.15.7/" asdf:*central-registry*)&lt;/code&gt; (аналог &lt;code&gt;PATH&lt;/code&gt;).&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-8118609682143392193?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/8118609682143392193/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=8118609682143392193' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8118609682143392193'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8118609682143392193'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/06/asdf-2.html' title='ASDF 2'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-6293976890675583971</id><published>2010-06-29T23:15:00.004+03:00</published><updated>2010-07-27T18:15:46.766+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Что такое стартап?</title><content type='html'>Или точнее, что отличает настоящий стартап от других видов бизнеса? Еще один вопрос, не имеющий никакого практического значения, но всегда являющийся интересной темой для поговорить :)&lt;br /&gt;&lt;br /&gt;После 2-х лет, в течение которых я более-менее имею отношение к этой сфере, сегодня пришла простая формулировка, что же я под ним понимаю.&lt;br /&gt;&lt;br /&gt;В общем, стартап — это начинание, в котором сочетаются и сбаллансированны 3 составляющих: исследовательская, инженерная и бизнес.  Исследовательская отвечает за сферу инноваций, инженерная — за их "доставку" до конечного потребителя, а бизнес — за извлечение из этого денег, позволяющих продолжать работу, развиваться и достигать личных целей основателям.  При этом тут, на самом деле, не важна сфера: инженерия сейчас нужна не только в Интернете :), но и в биохимии, и во флористике. (Вот, кстати, хорошая цитата про то, почему же отдельно исследование, а отдельно инженерия, случайно встреченная мною в статье в &lt;a href="http://en.wikipedia.org/wiki/Equal_transit-time_fallacy"&gt;Википедии&lt;/a&gt;: "The theory, while correct in as far as it goes, is not sufficient to allow one to do engineering").&lt;br /&gt;&lt;br /&gt;При этом про стартапы часто говорят, что в их основе лежит &lt;a href="http://youtu.be/xjDtI-d3jyo"&gt;масштабирование&lt;/a&gt;, но, как по мне, это естественное следствие того, что все 3 компоненты присутствуют и хорошо работают: исследования обеспечивают то, что стартап реально помогает людям, т.е. на его продукты есть масштабный спрос, инженерия обеспечивает возможность удовлетворить требуемый масштаб клиентов, а бизнес — донести до них информацию о такой возможности.&lt;br /&gt;&lt;br /&gt;А вот если одна из составляющих отсутсвует или не работает, как надо, то это уже не стартап а другого рода начинание, тем не менее, тоже имеющее право на жизнь (просто, нужно отдавать себе в этом отчет).  Если нет инновационной части, то это просто новый бизнес или даже просто проект (и это как раз основное большинство того, что сейчас называют "стартапами", скажем, веб-сайты служб знакомств). Если нет бизнес-составляющей, то это своеобразная форма творчества, только у инженеров, работа ради самого процесса, а не результата.  Впрочем, в таких случаях, как и с любым творчеством, иногда может получится что-то выдающееся.  Ну и, если нет инженерии, то это холостой выстрел, бизнес, который не состоится, а лишь расчистит дорогу для других.  Хотя чаще бывает, что все-таки удается восполнить этот недостаток: почти все бизнесмены понимают, что исполнение — это большая часть успеха.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-6293976890675583971?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/6293976890675583971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=6293976890675583971' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6293976890675583971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6293976890675583971'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/06/blog-post.html' title='Что такое стартап?'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-3589053673069502391</id><published>2010-06-16T15:59:00.001+03:00</published><updated>2010-06-16T16:01:49.617+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>Mathematicians vs computer scientists</title><content type='html'>&lt;blockquote&gt;There is a considerable difference between a mathematician’s view of the world&lt;br /&gt;and a computer-scientist’s. To a mathematician all structures are static: they have&lt;br /&gt;always been and will always be; the only time dependence is that we just haven’t&lt;br /&gt;discovered them all yet. The computer scientist is concerned with (and fascinated by)&lt;br /&gt;the continuous creation, combination, separation and destruction of structures: time is&lt;br /&gt;of the essence. In the hands of a mathematician, the Peano axioms create the integers&lt;br /&gt;without reference to time, but if a computer scientist uses them to implement integer&lt;br /&gt;addition, he finds they describe a very slow process, which is why he will be looking&lt;br /&gt;for a more efficient approach. In this respect the computer scientist has more in com-&lt;br /&gt;mon with the physicist and the chemist; like these, he cannot do without a solid basis in&lt;br /&gt;several branches of applied mathematics, but, like these, he is willing (and often virtu-&lt;br /&gt;ally obliged) to take on faith certain theorems handed to him by the mathematician.&lt;br /&gt;Without the rigor of mathematics all science would collapse, but not all inhabitants of a&lt;br /&gt;building need to know all the spars and girders that keep it upright. Factoring off cer-&lt;br /&gt;tain detailed knowledge to specialists reduces the intellectual complexity of a task,&lt;br /&gt;which is one of the things computer science is about.&lt;br /&gt;--&lt;a href="http://www.few.vu.nl/~dick/PT2Ed.html"&gt;"Parsing Technics"&lt;/a&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-3589053673069502391?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/3589053673069502391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=3589053673069502391' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3589053673069502391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3589053673069502391'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/06/mathematicians-vs-computer-scientists.html' title='Mathematicians vs computer scientists'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-8226967136218470334</id><published>2010-05-31T21:22:00.003+03:00</published><updated>2010-05-31T21:26:47.568+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Парадигмы программирования, followup</title><content type='html'>Полтора года назад написал про &lt;a href="http://lisp-univ-etc.blogspot.com/2008/12/blog-post.html"&gt;парадигмы программирования&lt;/a&gt;.  Сейчас подумал, что нужно называть вещи своими именами, а не так, как принято. :)&lt;br /&gt;&lt;br /&gt;Например, функциональный язык — это такой, где вы будете задачи решать через функции.  Т.е., когда вы беретесь за новую задачу, то напишите какой-нибудь defun или function.  Следующим ключевым свойством функциональных языков является вот что: все есть выражение (expression), т.е. возвращает значение, а не утверждение (statement).  Всё.  На этом функциональность, строго говоря заканчивается.  Но есть еще очень много других видов языков.  Вот Haskell, например, язык, вообще говоря, не такой уже функциональный язык.  Потому что решать вы все будете через типы и напишите сначала Data :: Type.  А функции — лтшь один из частных случаев этих типов (один из типов, попросту говоря), но ведь гораздо интереснее такие типы, как монады или функторы.  В общем, язык, на самом деле, алгебраический, т.е. для математиков (по складу ума).  Вообще, все языки более-менее для кого-то там по складу ума.&lt;br /&gt;&lt;br /&gt;Далее, вот, Common Lisp — наполовину функциональный, наполовину декларативный (потому что в половине случаев я начну не с defun, а с defmacro).  А еще на треть объектно-ориентированный, потому что для defclass тоже часто найдется место (но без фанатизма).  А Scheme — действительно чисто функциональный, так кроме функций больше ничего нет.  Дальше посмотрим на Erlang — это сперва язык для конкурентного программирования.  Всё закручено вокруг процессов и обмена сообщениями между ними.  Это только на поверхности там пролог и функции, это всего лишь синтаксис.  Т.е. нельзя отрицать функциональную ориентированность Erlang'а, но язык таки process-oriented/message-passing.  А что же этот знаменитый функциональный язык JavaScript?  Нет, нет и нет: "всё — выражение" не выполняется, и хотя функции — обычный синтаксис для записи кода, но ядро языка — это события и коллбеки, event-driven, как говорят американцы.&lt;br /&gt;&lt;br /&gt;Возьмем теперь Ruby.  Тут всё на классах и лямбдах, т.е. анонимных функциях.  А обычные функции превращены в сообщения.  Да еще и всё — выражение.  Вот такая анархия или мультипарадигменность.  И ко всему прочему полный контроль у программиста.  Получается объектно-procедурно-немного декларативно-ориентированный. Еще есть Python.  Тут все немного (или намного) регулярней.  Есть классы и функции, но не всё выражение, анонимных функций, считай нет.  В общем, glorified C (как хорошо показывет пример Cython), но, главное, что динамический и разумно простой.  Современный объектно-ориентированный процедурный язык, в общем.&lt;br /&gt;&lt;br /&gt;C Java, например, всё понятно.  Тут чистый классовый подход.  Класс на inner классе сидит и анонимным классом погоняет.  Говорят, это называется объектно-ориентированный подход, хотя это, вообще-то, про Smalltalk или даже про Python.  А тут у нас класс-ориентированный, чистой воды.  На закуску остается C++, которому можно всё, особенно в версиях Boost и 0x.  Только вот, забывают заметить, что это 3 отдельных языка: С, ++, а также еще Tempaltes.  И это не говоря про препроцессор.  Тут тоже, как бы, всё class-based, но всё — не объект, а указатель.  Короче говоря, такой машинно-ориентированный язык с системами типов и классов, в придачу к возможности застрелить себя в ступню, как говорят опять же американцы...&lt;br /&gt;&lt;br /&gt;Про brainfuck и все остальное более-менее без изменений.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-8226967136218470334?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/8226967136218470334/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=8226967136218470334' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8226967136218470334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8226967136218470334'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/05/followup.html' title='Парадигмы программирования, followup'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-6262131817516210865</id><published>2010-05-26T15:43:00.002+03:00</published><updated>2010-05-26T15:46:36.158+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Лисп — философия разработки</title><content type='html'>&lt;span id="for-and-date"&gt;&lt;strong&gt;Написано для: &lt;a href="http://fprog.ru/2010/issue5/vsevolod-dyomkin-lisp-philosophy/"&gt;Практика функционального программирования №5&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;Соавтор: &lt;a href="http://twitter.com/manzyuk"&gt;Александр Манзюк&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Чтобы не делать перепечатку, помещаю тут только аннотацию. Основной текст — по ссылке выше.&lt;br /&gt;&lt;br /&gt;В статье исследуются подходы к разработке, практикуемые в Лисп-среде, на примерах решения различных прикладных задач на языке Common Lisp. Вы узнаете о том, что макросы — это не только синтаксический сахар, но и прекрасный инструмент инкапсуляции, что кроме глобальных и локальных переменных бывают еще специальные, что полиморфные интерфейсы можно создавать без привязки к классам, а также о том, что определять стратегии передачи управления можно не только с помощью монад. Рассмотрены и решения прикладных задач: клиент для хранилища данных Redis, прокси между двумя бизнес-приложениями, внутренний API веб-сервиса, библиотека парсерных комбинаторов.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-6262131817516210865?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/6262131817516210865/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=6262131817516210865' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6262131817516210865'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6262131817516210865'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/05/blog-post_26.html' title='Лисп — философия разработки'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-3462153865846100189</id><published>2010-05-18T15:35:00.016+03:00</published><updated>2010-05-18T16:05:43.477+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Простая модель налоговой реформы для малого И крупного бизнеса</title><content type='html'>Я не буду касаться здесь каких-то сложных с экономической точки зрения идей и тем, таких как: нужно или не нужно НДС и т.п.  Взамен же попробую обратиться к столь общим вопросам, как: простота, эффективность, сбаллансированность и справедливость,— которых "немного" не хватает в текущей системе.  Отсюда, как по мне, и растут ноги у всех проблем.&lt;br /&gt;&lt;br /&gt;Итак, первое, единый налог и проблемы, связанные с его использованием для "оптимизации" налогообложения.&lt;br /&gt;&lt;br /&gt;Очевидно, что без упрощенной системы налогообложения весь бизнес в Украине вообще загнется или полностью уйдет в тень.&lt;br /&gt;&lt;br /&gt;Далее, не так очевидно, но легко понять, что в текущем виде упрощенная система — это проблема не только для государства (уход от налогов), но и для других форм бизнеса, а также... для самих предпринимателей.&lt;br /&gt;&lt;br /&gt;В чем проблема для других форм.  Простой пример: есть человек, который выполняет какую-то профессиональную деятельность, например, моет окна.  Он может делать это в рамках компании и получать зарплату 4000 грн в месяц, при этом налоговое бремя, которое ложиться на такую компанию — еще 2400 грн, а сам человек на руки получает 3400 грн (а 15% платит налога на доход).  Если он моет 20 окон в месяц, то за каждое окно компания должна брать с заказчика 320 грн (+ НДС :), чтобы хотя бы выйти в 0 (будем считать, что больше расходов нет).  Теперь представим, что тот же человек моет окна как частный предприниматель: теперь, чтобы получить свои 3400 грн, ему нужно выручить всего то 3600, т.е. при цене 320 грн за окно, помыть около 11 с хвостиком окон.  А если он помоет 20, то заработает 7800, что ровно в 2 раза больше.  Спрашивается, как компания сможет конкурировать с таким фрилансером?  Отсюда и зарплаты в конвертах, и "оптимизация" через СПД.  Т.е. виновато не только высокое налоговое бремя (хотя это виновник №1), но и несбаллансированная налоговая система (хотя это все-таки только №2).&lt;br /&gt;&lt;br /&gt;Второй момент: допустим человек моет окна самостоятельно, но не регистрируется как СПД, а платит обычный налог на доход (15%).  Хотя такая ситуация немного противоречит закону, однако, по сути, это те условия, в которых находятся все наемные работники, если вообще не принимать в рассмотрение компании.  Т.е. наемные работники тоже проигрывают СПДшникам на эти 15%.&lt;br /&gt;&lt;br /&gt;Теперь рассмотрим вопрос пенсий.  СПД-шник платит в пенсионный фонд 84 грн.  В то же время любой наемный работник платит 33,2% своей зарплаты, что составляет минимум 300 грн.  В результате оба будут получать одинаковую социальную пенсию.  Но даже, если человек будет иметь сногсшибательную зарплату и платить огромные отчисления в ПФ, никто не гарантирует, что через 30 лет он будет получать какую-то громадную пенсию, как и сейчас не получают большой пенсии советские герои труда, проработавшие по 50 лет на каких-то вредных производствах и т.д.  Потому что государство у нас такое, ответсвенное.  Зато из этих отчислений будет платиться огромная пенсия сегодня депутатам, секретным агентам и академикам.  Это, ясное дело, абсолютно не справедливо и неэффективно.  Как эффективнее — ниже.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Итак, существование СПД-формы — это громадная несправедливость по отношению к: 1. компаниям, 2. простым работникам 3. будущим пенсионерам.&lt;br /&gt;С другой стороны, проблема не столько в ней, сколько в гигантском налоговом бремени на компании, точнее собственников (почти 60% начисления на зарплату наемных рабочих + 25% налог на дивиденды, который, фактически, является двойным налогообложением).  Это бремя приводит к тому, что блокируется рост зарплат и происходит тенезация экономики.  А в пенсионном фонде все равно дырка, которая покрывается из бюджета.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Предложения по решению проблемы исходят из простейших идей:&lt;br /&gt;1. СПД — это самозанятый профессионал, который делает работу самостоятельно.&lt;br /&gt;2. Пенсии должны быть справедливыми (во всех отношениях :)&lt;br /&gt;&lt;br /&gt;Соответственно:&lt;br /&gt;&lt;br /&gt;1. СПД форма должна существовать и еще более упроститься.&lt;br /&gt;- Во-первых, в области видов деятельности.  Зачем искусственно ограничивать людей: есть специалисты широкого профиля, почему не дать им заниматься теми разными занятиями, которые им по душе?  Т.е. КВЕД можно оставить только для статистики, но ни в коем случае не делать его ограничением, как сейчас.&lt;br /&gt;- Во-вторых, уплата налога должна быть не такой частой (хотя бы раз в квартал), особенно в связи с п.2&lt;br /&gt;- В-третьих, нужно убрать верхнее ограничение на размер выручки (если специалист мирового класса зарабатывает миллионы, он что, перестает быть предпринимателем?), но в то же время СПД не должен использовать наемный труд.  Вообще.  Если нужны помощники, пускай тоже оформляются как СПД и работают по подрядному принципу.  Если у вас есть сотрудники, то это уже компания.  Просто, у нас создание компании является нереально сложным делом.&lt;br /&gt;&lt;br /&gt;2. Ставка налога на СПД должна быть приравнена к налогу на доход физлиц.  Это, во-первых, справедливо по отношению к наемным работникам, во-вторых, выгодно для государства.  Ну и, в-третьих, не вызовет принципиальных возражений у любого предпринимателя, который понимает, что налоги платить надо, но они должны быть подъемными.  В то же время это будет справедливо к тем, кто по каким-то причинам не работает на данный момент как предприниматель: нет прибыли — нет налога.  Вообще, предприниматель, который временно (от 1 квартала) сидит без работы, также должен иметь право стать на биржу труда, как и обычный уволенный сотрудник и какое-то время получать социальное пособие.  О соцопсобии — ниже.&lt;br /&gt;&lt;br /&gt;3. Сферу соцфондов можно радикально упростить и, при этом, сохранить существующие пенсии (потерять которые так боятся чиновники).  Все трубят о втором уровне пенсионной системы, но никто не пытается доходчиво объяснить, что это такое.  На самом деле все должно выглядеть очень просто: должна быть единая, не зависящая от зарплаты, социальная пенсия, которая выплачивается всем нетрудоспособным людям из ПФ, а также социальная помощь по безработице от государственной страховой компании.  Обе эти величины должны равняться прожиточному минимуму.  Помощь от несчастных случаев тоже должна быть стандартизована и зависить только от категории (тяжести) травмы, а не от зарплаты.  Таким образом, имея статистику по количеству работающих, пенсионеров, безработных и несчастных случаев, можно рассчитать необходимую величину единого социального взноса с каждого работающего (не зависимо от того, сотрудника компании, или СПД, или даже рантье) на бюджетный год для выхода на нужные цифры компенсаций для неработающих.  (Т.е. для СПД помимо налога на доход добавляется единый социальный взнос, меняющийся из года в год. Такой взнос нужно будет платить раз в месяц или же наперед).&lt;br /&gt;&lt;br /&gt;А как быть с пенсией, которая выше социальной и т.п.? Для этого и существует второй уровень пенсионной системы, как и второй уровень страховой системы.  Он состоит из &lt;b&gt;добровольных&lt;/b&gt; отчислений работающих сверх обязательного социального взноса.  Естественно, такие добровольные отчисления могут делаться как в государственный ПФ и СК, так и в негосударственные по выбору страхователя.  И они должны быть персонализированны.  Впрочем, такие отчисления могут делать за сотрудников и компании (опять же добровольно), и иметь в связи с этим какие-то налоговые льготы и т.п. (это уже вопрос экономических моделей).  Также, например, государство может делать такие отчисления для всего госаппарата, чтобы сохранять существующую сейчас систему высоких пенсий чиновникам — это будет его конкурентным преимуществом на рынке труда :)&lt;br /&gt;&lt;br /&gt;А как покрывать дефицит ПФ для выплаты текущих обязательств государства сверх социальной пенсии (средства на которую, напомню, собираются через единый социальный взнос): пенсий, которые уже начисленны пенсионерам, которые, разумеется, не могут делать никаких отчислений?  На самом деле, также как и сейчас — из госбюджета и это будут очень большие деньги :)  Но хорошая новость в том, что с введением такой пенсионной системы обязательства бюджета будут с каждым годом уменьшаться, как и список тех пенсионеров, которые будут получать пенсию старого образца.  Остается вопрос, как быть с теми, кому 54, 58, 50?  Для них (как и для всех остальных работающих) можно ввести прогрессивную шкалу пенсии, которую уже должно государство: разделить количество роработанных лет на полный стаж до пенсии и получить процент от той пенсии, на которую человек мог рассчитывать по старой системе.  Т.е. для человека, которому остался работать год, а он проработал 24, это будет 96%, а для того, кто проработал 2, а еще впереди 32 это будет 4%.  Это обязательство государства, как и перед текущими пенсионерами. Остальную сумму можно будет набирать за счет добровольных взносов.&lt;br /&gt;&lt;br /&gt;Таким образом, на мой взгляд, пенсионная система станет не только справедливой, но и подъемной для бизнеса (точнее, бизнес вообще будет устранен от субсидирования ПФ), и, в то же время, можно будет сохранить статус кво для современных пенсионеров.  А то, что все это будет субсидироваться из бюджета — так, во-первых, оно и сейчас так происходит, а, во-вторых, это, на мой взгляд, оправданная общесоциальная жертва за радикальное улучшение социально-экономической ситуации в стране.  И все остальные налоговые проблемы — это, просто, детский лепет по сравнению с этой....&lt;br /&gt;&lt;br /&gt;4. Наконец, последнее: нужно радикально упростить создание и, что немаловажно, ликвидацию компаний (ООО).  Во-первых, администрирование этих процессов: тут можно очень долго говорить.  Во-вторых, снизить ограничение по нижнему размеру уставного фонда (неподъемное для многих бизнесов вначале деятельности).  Можно говорить, что есть всякие командитные общества, но это же не полноценная бизнес-форма...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-3462153865846100189?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/3462153865846100189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=3462153865846100189' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3462153865846100189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3462153865846100189'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/05/blog-post.html' title='Простая модель налоговой реформы для малого И крупного бизнеса'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-4365568938532865660</id><published>2010-03-28T09:45:00.000+03:00</published><updated>2010-03-28T09:47:59.331+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>GUI в Common Lisp — еще один миф</title><content type='html'>&lt;span id="for-and-date"&gt;&lt;strong&gt;Написано для:&lt;/strong&gt; &lt;a href="http://habrahabr.ru/blogs/lisp/89097/"&gt;habrahabr.ru&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Бытует расхожее мнение, что в Common Lisp нет или же плохая поддержка графики.  Это еще один миф из серии, что Lisp — это язык только для подсчета факториалов.  На самом деле, как и в большинстве других прикладных сфер общего назначения (например, веб, форматы передачи данных, взаимодействие с БД и т.д.) в Lisp-среде есть полный спектр библиотек и тулкитов для всех основных платформ с разными уровнями абстракции.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Linux/Unix&lt;/h4&gt;&lt;br /&gt;Базовой библиотекой для графики из Common Lisp под Unix является &lt;a href="http://www.cliki.net/CLX"&gt;CLX&lt;/a&gt;.  Это аналог xlib, т.е. низкоуровневый клиент, напрямую общающийся по X-протоколу.&lt;br /&gt;&lt;br /&gt;Кроме того, есть обертки для основных графических фреймворков разной степени зрелости: LTK, CL-GTK2, CommonQt, CL-CAIRO2.  Лично мне доводилось иметь дело с LTK, и работа с ним тривиальна.  Хороший пример приложения, его использующего — простой и удобный Lisp-редактор/REPL &lt;a href="http://common-lisp.net/project/able/"&gt;ABLE&lt;/a&gt;.&lt;br /&gt;&lt;img src="http://img.photobucket.com/albums/v473/pufpuf/able.png" alt="ABLE screenshot"/&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Windows&lt;/h4&gt;&lt;br /&gt;Кроме возможности использовать кросс-платформенные фреймворки из прошлого раздела, есть еще &lt;a href="http://www.lispworks.com/products/capi.html"&gt;LispWorks CAPI&lt;/a&gt;, о котором только положительные отзывы.  Единственная особенность заключается в том, что, как и большинство профессиональных сред разработки на любых языках под Windows, LispWorks стоит довольно дорого (ок. 1200 $), а CAPI доступна только в профессиональной версии.  (Впрочем, попробовать его можно и в trial версии).&lt;br /&gt;&lt;br /&gt;Также есть CL-OPENGL, которая, разумеется, кросс-платформенная.&lt;br /&gt;&lt;br /&gt;&lt;habracut /&gt;&lt;h4&gt;MacOS X&lt;/h4&gt;&lt;br /&gt;Дополнительно к Unix-библиотекам для MacOS X есть хорошие &lt;a href="http://trac.clozure.com/ccl/wiki/Cocoa"&gt;Cocoa-биндинги&lt;/a&gt; в Clozure CL.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Специфические Lisp-решения&lt;/h4&gt;&lt;br /&gt;Библиотека &lt;a href="http://common-lisp.net/project/mcclim/"&gt;McCLIM&lt;/a&gt; реализует Lisp Interface Manager спецификацию, определяющую весьма развитый протокол по оперированию с графическими примитивами.  Хотя спецификация является платформо-независимой, сама библиотека на данный момент основанна на CLX с вытекающими отсюда последствиями пригодности только для Unix-среды.  Если это не есть ограничением, то именно она нужна вам, если вы собираетесь писать что-то, сильно завязанное на графику: игру, графический или CAD-редактор, или же новый &lt;s&gt;Emacs&lt;/s&gt;&lt;a href="http://common-lisp.net/project/climacs/"&gt;Climacs&lt;/a&gt;.&lt;br /&gt;&lt;img src="http://img.photobucket.com/albums/v473/pufpuf/climacs.png" alt="Climacs" /&gt;&lt;br /&gt;&lt;br /&gt;Оригинальным подходом к GUI, зародившимся в Lisp-среде является проект &lt;a href="http://common-lisp.net/project/cells/"&gt;Cells&lt;/a&gt;, который переносит spreadsheet-парадигму взаимозависимых "ячеек" на графический интерфейс.  У него несколько Lisp-реализаций c тем или иным бэкендом: CELLS-GTK, CELLTK, CELLO,— а также есть и порты на другие языки.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Выводы&lt;/h4&gt;&lt;br /&gt;В общем, варианты есть на любой вкус и запросы.  С чего начать?  Для простого GUI я бы выбрал &lt;a href="http://www.peter-herth.de/ltk/"&gt;LTK&lt;/a&gt; или &lt;a href="http://common-lisp.net/project/cl-gtk2/"&gt;CL-GTK2&lt;/a&gt;.  Оба они кросс-платформенные.  Первый по причине максимальной простоты, даже примитивности.  Соответственно, и подходит он для примитивных приложений.  Второй — потому что это хорошая обертка для современной объектно-ориентированной графической библиотеки,  активно развивающаяся, да еще и с русским автором :)&lt;br /&gt;&lt;br /&gt;PS. Еще несколько более специфических графических библиотек, конечно, можно найти на &lt;a href="http://www.cliki.net/graphics%20library"&gt;Cliki&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-4365568938532865660?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/4365568938532865660/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=4365568938532865660' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/4365568938532865660'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/4365568938532865660'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/03/gui-common-lisp.html' title='GUI в Common Lisp — еще один миф'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-189474775432796726</id><published>2010-03-27T12:31:00.002+02:00</published><updated>2010-03-27T12:34:47.392+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Пару мыслей об SCTest</title><content type='html'>Как по мне, ценность этого мероприятия даже не в том, что люди могут получить фидбек по своим проектам или потренировать презентации (это можно сделать и другим путем, подчас более эффективным), а в том, что оно создает определенную "школу" стартапов, т.е. происходит передача знаний, опыта, информации от старших коллег, чего-то добившихся, состоявшихся личностей и предпринимателей.  На каждом мероприятии приходит кто-то интересный с собственным подходом и взглядом на вещи.  Как и в любой школе, тут есть свои гуру, чьи слова постоянно цитируют и в которых находят ответы на все вопросы.  В нашем случае это, конечно, Стив Бленк.&lt;br /&gt;&lt;br /&gt;В этот раз панель была очень показательна, поскольку на ней присутствовали представители 3-х основных категорий участников test'а:&lt;br /&gt;- состоявшийся, как правило, без сторонней помощи (венчурных инвесторов), а благодаря личным качествам и работе, предприниматель.  Как раз пример того, у кого есть чему поучиться;&lt;br /&gt;- tru стартапер, т.е. человек, хорошо изучивший стартап-"технологии", как правильно надувать пузырики, что нужно говорить инвесторам и т.п.  (Эти качества, по моему мнению, на самом деле независимы от настоящих бизнес-качеств, которые присущи первой категории людей, так что "настоящий стартапер" может как ими обладать, так и не обладать);&lt;br /&gt;- startup kiddie (по аналогии со &lt;a href="http://en.wikipedia.org/wiki/Script_kiddie"&gt;script kiddie&lt;/a&gt;), т.е. молодой человек, увлеченный хайпом стартап-движения, и порой считающий, что сможет в этой игре переиграть "старших товарищей" (упреждиск: если относить меня к какой-то из этих категорий, то разве что к этой).&lt;br /&gt;&lt;br /&gt;Интересна реакция на выступление представителя M$ — как раз синдром startup-kiddies, которые всё знают и всё могут.  Я не говорю о том, что у большинства из пристуствующих на компьютере стоит ворованная копия той самой Windows (упреждиск: пишу это на Linux).  Дело в другом: выступал отличный профессионал, с местами интересным докладом, содержащим много полезной информации (конечно, не без корпоративщины и прямой рекламы, а также самолюбования: оказывается, Vista была провалом, потому что Intel с чипсетами подкачал :)  А реакция была не поискать интересное, а поерничать, когда же он будет кричать "Developers, developers...", и поприкалываться, что человек не умеет пользоваться PowerPoint'ом (что в данном случае было банальной глупостью).  Элементарной культуры и самоуважения не хватает...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-189474775432796726?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/189474775432796726/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=189474775432796726' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/189474775432796726'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/189474775432796726'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/03/sctest.html' title='Пару мыслей об &lt;a href=&quot;http://sctest.org&quot;&gt;SCTest&lt;/a&gt;'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-2145999776834562139</id><published>2010-03-15T09:29:00.005+02:00</published><updated>2011-10-27T10:07:30.322+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>What's on Coders' Minds?</title><content type='html'>Recently I've finished reading Coders at Work by Peter Siebel — another excelent book, that has given me a lot of food for though concerning programming.  This weekend I've decided to perform a little analytics on it to see common patterns.  I've collected statistics on the questions of programming language use, interrelations between them and topics, that might be relevant for the sequeals (there are so many other great programmers out there in the wild :)&lt;br /&gt;&lt;h2&gt;1. Programming language preferences&lt;/h2&gt;First comes C, that was mentioned by all the coders.  Only Peter Norvig didn't speak about it in some detail.  It seems, that everyone had at least medium experience programming in it (except maybe for Norvig and Fran Allen), which proves, that the language truly is the Lingua Franca of programming community.&lt;br /&gt;&lt;br /&gt;Next in terms of mentions comes C++, Java and Lisp, that were mentioned by more than 12 people.&lt;br /&gt;&lt;br /&gt;C++ netted mostly negative or at least neutral reviews.  Moreover, it seems, that only a couple of people had any big real-world coding experience in it: others (like Jamie Zawinski) tried to avoid using it at all cost.&lt;br /&gt;&lt;br /&gt;On the contrary at least 4 of the people had a substantial hands-on involvement in Lisp programming, but mostly in the past decades.  Others mentioned it more as an important alternative or a design influence.&lt;br /&gt;&lt;br /&gt;The same amount of coders were really experienced in the Java world.  Others just mentioned it as either the recent derivative of the Algol-family, or as a typical example of object-oriented approach, or for its automatic memory-management capability.&lt;br /&gt;&lt;br /&gt;Next comes a pack of languages, that are mostly in wide use today, some gradually falling from grace and some getting more traction.  These include: Fortran, Assembler, Python, JavaScript, BASIC, Perl and Pascal.  All were mentioned by 6 to 10 people.  Among them Assembler, BASIC and Pascal are obviously mostly out of use, but were the three most mentioned first languages in someone's carrers.  And Fortran has it's distinct role as the default scientific language (and as the first high-level language).&lt;br /&gt;&lt;br /&gt;The next group of languages were mentioned rarely, but they were supported by strong advocates.  These are Haskell, Smalltalk, and to a lesser extent Scheme and the ML family.  Also the same amount of mentions went to the now defunct previously important ones: Tcl, APL, PL/I, BCPL, and COBOL.&lt;br /&gt;&lt;br /&gt;Interestingly enough some of the most wide-spread languages of today: PHP, C#, and Ruby,— were mentioned only once or twice and only in passing.  The same concerns 3 other more or less important contemporary languages: Objective-C, Scala and Erlang,— that got only a single mention.  The reasons for that seem to be different, but one of them can be, that bright representatives of those languages' communities (except for Erlang's creator Armstrong) were not interviewed.&lt;br /&gt;&lt;br /&gt;Finally, a host of other languages, not in use today, got mentioned at least twice.  They are: Ada, Algol, Eiffel, E, Prolog, Self, Simula, SNOBOL and Teco.&lt;br /&gt;&lt;h2&gt;2. Social graph&lt;/h2&gt;&lt;img src="http://img.photobucket.com/albums/v473/pufpuf/coders-1.png" alt="Coders social graph" /&gt;&lt;br /&gt;&lt;br /&gt;4 of the coders had some Berkley background, 3 — MIT and 2 — CMU. Among the most important organizations were Google (4 coders work there and 4 other mentioned its practices), Microsoft (1 employee and 7 other having some problem with them), IBM (1 employee, mostly everyone's first computer :), Xerox PARC and Netscape/Mozilla.&lt;br /&gt;&lt;br /&gt;Also worth mentioning is that 13 of 15 interviewed programmers were from the USA, 2 from Europe.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;3. Recommended books&lt;/h2&gt;* SICP (5 mentions)&lt;br /&gt;* The Art of Computer programming (obviously) (5 mentions)&lt;br /&gt;* Design Patterns (3), although all spoke of it as at least controversial&lt;br /&gt;* The psychological books: "The Mythical Man-Month" and "Psychology of Computer Programming", each brought up both by Steele and Bloch (2 mentions)&lt;br /&gt;&lt;br /&gt;And once were recommended these books:&lt;br /&gt;* Beautiful Code: Leading Programmers Explain How They Think, Andy Oram, Greg Wilson (eds.) (O’Reilly, 2007)&lt;br /&gt;* Code Complete, Steve McConnell (Microsoft Press, 1993)&lt;br /&gt;* Compiling with Continuations, Andrew W. Appel (Cambridge University Press, 1992)&lt;br /&gt;* The Design and Analysis of Computer Algorithms, Alfred V. Aho, John E. Hopcroft, and Jeffrey D. Ullman (Addison-Wesley, 1974)&lt;br /&gt;* The Elements of Programming Style, Brian Kernighan and P.J. Plauger (Computing McGraw-Hill, 1978)&lt;br /&gt;* Elements of Style, William Strunk and E.B. White (Longman, 1999)&lt;br /&gt;* Hacker’s Delight, Hank Warren (Addison-Wesley, 2002)&lt;br /&gt;* Higher-Order Perl, Mark Jason Dominus (Morgan Kaufmann, 2005)&lt;br /&gt;* Java Concurrency in Practice, Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, and Doug Lea (Addison-Wesley, 2006)&lt;br /&gt;* Java Puzzlers: Traps, Pitfalls, and Corner Cases, Joshua Bloch and Neil Gafter (Addison-Wesley, 2005)&lt;br /&gt;* Programming Pearls, Jon Bentley (ACM Press, 1999)&lt;br /&gt;* Purely Functional Data Structures, Chris Okasaki (Cambridge University Press, 2008)&lt;br /&gt;* Zen and the Art of Motorcycle Maintenance: An Inquiry into Values, Robert Pirsig (Bantam, 1984)&lt;br /&gt;&lt;br /&gt;The only unexpected omission seem to be "Goedel, Escher, Bach: an Eternal Golden Braid", which is the favorite programming book of many.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;4. Other things&lt;/h2&gt;The tools in use include Emacs (most mentioned) and GDB.  Others were: Java IDEs (Eclipse, NetBeans and IntelliJ), profilers, and correctness checkers, like JSLint, Valgrind and QuickCheck.&lt;br /&gt;&lt;br /&gt;There were also a few topics, brought up by many of the coders, apart from the ones, explicitly triggered by the interview questions.  Those are API and GUI design (often in connection with the ugliness of WinAPI), transactional memory (and other ways of tackling the massively multicore world), and means of inter-process communication and data-interchange.  This may be a suggestion for questions of the future interviews.&lt;br /&gt;&lt;br /&gt;Some of the coders came to programming through games or programmed some.  2 times were mentioned Pac-Man and the Game of Life.  Also variants of tic-tac-toe, tetris, and Adventure were named.  Still game programming is one of the most important parts of the programming industry and no prominent person from it was present (although John Carmack was planned).&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;5. Conclusions (who to interview next?)&lt;/h2&gt;The next candidates can be derived from the mentions' graph, but this can lead to confinement inside of the subset of all programming communities due to the cluster nature of "schools" of influence.  So it's also worth considering the missing languages and georgaphical diversity.&lt;br /&gt;&lt;br /&gt;So, among the important pears of our coders were:&lt;br /&gt;* Richard Stallman, founder of the FSF and GNU (3 mentions)&lt;br /&gt;* Guido van Rossum, creator of Python (3)&lt;br /&gt;* Bjarne Stroustrup, creator of C++ (3)&lt;br /&gt;* Danny Bobrow, a prominent lisper (3)&lt;br /&gt;* Larry Wall, creator of Perl (2)&lt;br /&gt;* Adele Goldberg, who was at the root of object-oriented movement (2)&lt;br /&gt;* Bill Gosper, another prominent lisper (2)&lt;br /&gt;&lt;br /&gt;Also were mentioned the famous computer scientists: Edsger Dijkstra, Tony Hoare and Noahm Chomsky.&lt;br /&gt;&lt;br /&gt;It can be seen, that the mentioned persons were mostly programming language designers.  But this book showed, that it is equally important to present the opinion of language users, because in many areas it can diverge.  In search of such people we can direct ourselves to the "missing languages" side.  And the first one of those is actually C++ for its wide spread is not reflected by the interviewed, who were important representatives of that school of thought.  Apart from language's creator Stroustrup, 2 people come to mind: John Carmack of idSoftware fame and Raymond Chen from Microsoft.  The Ruby camp has another interesting feature: it's main proponents are very loudly heard in the programming world.  David Hanson, Dave Thomas, Reginald Braithwhite, Tim Bray and Chris Wanstrath (Github) all have influential blogs.  The other one famous Ruby programmer, whom it could be really interesting to interview, is Why the Poignant Stiff.  It would also be interesting to hear the voice of those "unknown" heroes of the PHP and C# world, because it can resonate with lots and lots of programmers in the world.  From other (well represented in the previous book) communities the prominent figures are Ian Bicking (Python), Edi Weitz (Lisp), Ward Cunningham (Java) and Luke Gorrie (Erlang, Smalltalk, Lisp).&lt;br /&gt;&lt;br /&gt;This book also featured a number of interviews with computer scientists: Knuth and Allen, as well as Norvig, Steele and Peyton Jones (although those three are more on the practical side).  Among their pears in the CS world, obviously, the most mentioned were Abelson and Sussman.  Next come Danny Bobrow, Aho and Ulman (among the living).&lt;br /&gt;&lt;br /&gt;Talking about geographical diversity, Edi Weitz and Luke Gorrie represent Europe.  Other interesting European coders are the creators of PHP Rasmus Lerdorf, Prolog Alain Colmerauer and OCaml Xavier Leroy.  And in every programming community there can easily be named prominent European hackers (like Ola Bini from JRuby team).  It is also worth "visiting" Japan, the former USSR (that had it's own tradition in CS and software), and, possibly, India and China.  From Russia such people as: Eugene Roshal (creator of the Rar compression format), Stepan Pachikov (founder of Evernote), and Viktor Shchepin (author of ejabberd Erlang-based jabber server),— can be named.&lt;br /&gt;&lt;br /&gt;Finally, the last diversity question, that was raised by &lt;a href="http://www.gigamonkeys.com/blog/2009/09/22/women-coders.html"&gt;Peter Siebel himself&lt;/a&gt;, is women participation.  The first woman candidate, that can be derived from the analysis, is Adele Goldberg.  One other woman, that comes to mind, is Allison Randal, working on the Parrot virtual machine and Perl 6 programming language.&lt;br /&gt;&lt;br /&gt;(Actually most of the mentioned names were in the list of possibil candidates for the first book).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-2145999776834562139?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/2145999776834562139/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=2145999776834562139' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/2145999776834562139'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/2145999776834562139'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2010/03/whats-on-coders-minds.html' title='What&apos;s on Coders&apos; Minds?'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-7020765686577939431</id><published>2009-11-07T23:41:00.005+02:00</published><updated>2011-10-20T15:46:10.405+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>О страшных монадах</title><content type='html'>Всегда полезно &lt;a href="http://lisp-univ-etc.blogspot.com/2009/11/haskell.html"&gt;поспорить&lt;/a&gt;, чтобы еще раз озаботиться своим пониманием темы. Поэтому я решил снова взглянуть на монады и попытаться ответить на вопрос, в чем их проблема (а также попутно понять, в чем от них польза).&lt;br /&gt;&lt;br /&gt;Как верно было &lt;a href="http://rssh.livejournal.com/142551.html"&gt;подмечено&lt;/a&gt;, монады &amp;mdash; это очень просто. А еще монады &amp;mdash; везде. Почему же тогда у &lt;a href="http://zabivator.livejournal.com/364173.html"&gt;многих&lt;/a&gt; с ними проблемы?&lt;br /&gt;&lt;br /&gt;1. Терминология&lt;br /&gt;Где-то написано, что использование нетипичной для предметной области терминологии &amp;mdash; это аттрибут троллинга (к сожалению, не удалось найти ссылку). Можно возразить: "да ведь монады &amp;mdash; это же математический термин". Да, но математика и инженерия (в частности, программирование) &amp;mdash; разные дисциплины. Математики не строят мосты, да. А термин монада не относится к предметной области программирования. В ней есть такое понятие как "вычисление" (computation). (Пусть меня осудят, но) фактически, монада в Haskell &amp;mdash; это &lt;b&gt;стратегия вычисления&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Почему же тогда говорят "поместить/извлечь значение в/из монаду/ы"? Говорят так для простоты. На самом деле, можно лишь запустить определенное вычисление с определенным входным значением (отсюда и идиома run...). Слишком похоже на "запустить процедуру", не так ли?&lt;br /&gt;&lt;br /&gt;Так что с термином "монада" либо создатели Haskell были недостаточно дальновидны, либо, просто, хотели эпатировать программистский мир.&lt;br /&gt;&lt;br /&gt;2. Подход к изучению&lt;br /&gt;К сожалению, большинство объяснений монад так или иначе отталкиваются от того, что монада &amp;mdash; это всего навсего тип и 2 простые функции. Но проблема в том, что от того, что вы поймете, как работают функции return и bind, вам не станет сразу ясно, зачем они нужны вместе. Отталкиваться в понимании того, что такое &lt;strike&gt;монады&lt;/strike&gt; стратегии вычисления, нужно от того, зачем они нужны? Сейчас я сделал именно так, и все легко стало на свои места.&lt;br /&gt;&lt;br /&gt;Какие бывают стратегии вычисления? Я сейчас не буду называть самую важную из них, а напишу о ней в следующей записи &amp;mdash; пока предлагаю прийти к ответу самостоятельно. Могут быть: вычисление, которое завершается неудачно, если неудачно завершается хотя бы одно из внутренних подвычислений &amp;mdash; это знаменитая монада Maybe, которую мы сейчас рассмотрим. Может быть вычисление, которое использует какое-то доступное для всех подвычислений "хранилище" данных &amp;mdash; State. Есть вычисление, которое связанно с обменом данными с внешней средой &amp;mdash; IO и т.д.&lt;br /&gt;&lt;br /&gt;Бытует мнение, что Haskell &amp;mdash; ленивый язык (более того, это официальная позиция). На самом деле, все немного сложнее или проще, зависит от того, как посмотреть. Во-первых, всем понятно, что абсолютно (или, как говорится, чисто) ленивого языка быть не может, поскольку в таком случае мы никогда не получим какого-то результата работы программы. Каким образом Haskell'исты выходят из этого затруднения? Принято говорить, что, в целом, язык ленивый, а вся неленивость/энергичность (eagerness) находится (так и хочется сказать, заточЕна) внутри монад. Но дело в том, что любая Haskell-программа находится внутри монады IO. В итоге оказывается, что ленивыми в Haskell являются только чистые функции, а все вычисления (в понимании программистском), которых, на самом деле, большая часть &amp;mdash; все равно остаются энергичными. В то же время любая чистая Haskell-функция, учитывая pattern matching и немутируемые переменные, по большому счету представляют из себя одну case-конструкцию и вызов других функций. Красиво? Часто, да. Однобоко? Тоже. ОК, но самое интересное дальше.&lt;br /&gt;&lt;br /&gt;И в других языках примерно то же самое! По сути, разница только в том, что:&lt;br /&gt;(а) не проведена четкая граница между ленивыми вычислениями и энергичными. Впрочем, каждый может для себя ее проводить сам: например, можно думать о ленивых вычислениях как об аттрибутах "математических" функций (и таким образом их и программировать), а об энергичных &amp;mdash; как о процедурах. В любом языке может быть реализована ленивая стратегия вычислений, однако не везде это сделать одинаково легко...&lt;br /&gt;(б) программист самостоятельно может строить процесс вычисления адекватный конкретной задаче. По сути, построение вычислительных процессов и есть одно из главных составляющих собственно того, что мы понимаем под программированием&lt;br /&gt;&lt;blockquote&gt;Можно выдвинуть предположение, что &lt;b&gt;все разные виды вычислений можно свести к &lt;strike&gt;нескольким десяткам&lt;/strike&gt; определенному количеству монад&lt;/b&gt;. И, изучив их, можно будет составлять вычисления, как кубики в конструкторе. (И это предположение действительно выдвигается). В таком случае монады и вправду &lt;b&gt;становились бы решающим методом абстракции в программировании&lt;/b&gt;. Впрочем, оказывается, что даже сведя вычисления к монадам, составлять их также легко, как кубики, не удается, и приходится прибегнуть к помощи еще одних монад &amp;mdash; монадных преобразователей (monad transformers). В итоге, монада на монаде сидит и монадой погоняет... :) (Можно было догадаться исходя из принципа вычислительной эквивалентности. Вообще говоря, приняв во внимание этот принцип, можно понять, что максима &lt;a href="http://thesz.livejournal.com/645774.html"&gt;"there is no silver bullet"&lt;/a&gt; таки справедлива всегда).&lt;br /&gt;И еще одно: вывод про сведение всех вычислений к определенному набору не нов. Он лежит в основе структурного программирования, где доказано, что все вычисления можно свести к комбинации последовательного выполнения, условного выражения и цикла.&lt;/blockquote&gt;&lt;br /&gt;Вот тут то разница между Haskell'ем и другими языками становится наиболее существенной: другие языки имеют синтаксическую поддержку определенного узкого набора монад, и дают возможность создавать другие монады ad hoc с использованием того или иного механизма. Haskell, по сути, делает то же самое, только создание новых монад регуляризирует и вгоняет в определенные рамки. Итак Haskell &amp;mdash; это язык, в котором дисциплина на первом месте.&lt;br /&gt;&lt;br /&gt;В общем, &lt;b&gt;концепция монад очень полезна, чтобы понять, что могут быть совершенно любые стратегии вычислений&lt;/b&gt; и не стоит считать, что возможны только те, которые определенный язык считает родными (вот, в С не было стратегии вычисления Exception, а в С++ появилась :)&lt;br /&gt;&lt;br /&gt;Возвращаясь к Maybe. Его все понимают, так ведь? Это нужно для того, чтобы если где-то в последовательности вычислений у нас получился элемент Null (Nothing), это значение в итоге вернулось в качестве результата, и при этом другие части вычисления не имели проблем с обработкой такого значения.&lt;br /&gt;&lt;br /&gt;Этот шаблон хорошо знаком всем, кому доводилось встречаться с &lt;a href="http://en.wikipedia.org/wiki/Short-circuit_evaluation"&gt;логическими операторами с коротким замыканием&lt;/a&gt;. Идеально он работает в Common Lisp, где значение nil также является логической ложью (это, кстати, считается многими одним из кардинальных преимуществ Common Lisp над Scheme, в котором эти 2 значения разделены). Ниже приводится пример реализации Maybe на CL, аналогичный примеру из &lt;a href="http://www.haskell.org/all_about_monads/html/meet.html"&gt;пособия по монадам&lt;/a&gt;:  &lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;(defmacro maybecall (val &amp;rest funs)&lt;br /&gt;  "Consequently apply a sequence of FUNctionS to the&lt;br /&gt;return value of the previous computation, starting with VAL"&lt;br /&gt;  `(and-it ,val&lt;br /&gt;           ,@(mapcar (lambda (fun)&lt;br /&gt;                       `(funcall ,fun it))&lt;br /&gt;                     funs)))&lt;br /&gt;&lt;br /&gt;;; где:&lt;br /&gt;(defmacro and-it (&amp;rest args)&lt;br /&gt;  "Like AND, but IT is bound to the value of the previous computation"&lt;br /&gt;  (cond ((null args) t)&lt;br /&gt;        ((null (tail args)) (first args))&lt;br /&gt;        (t `(let ((it ,(first args)))&lt;br /&gt;              (when it&lt;br /&gt;                (and-it ,@(tail args)))))))&lt;br /&gt;&lt;br /&gt;;; работает?&lt;br /&gt;&lt;br /&gt;(defun mother (x)&lt;br /&gt;  (gethash x *mothers*))&lt;br /&gt;&lt;br /&gt;(defun father (x)&lt;br /&gt;  (gethash x *fathers*))&lt;br /&gt;&lt;br /&gt;(setf (gethash :b *fathers*) :e&lt;br /&gt;      (gethash :a *fathers*) :d&lt;br /&gt;      (gethash :b *mothers*) :c&lt;br /&gt;      (gethash :a *mothers*) :b)&lt;br /&gt;&lt;br /&gt;(maybecall :a #'mother #'father) =&gt; :e&lt;br /&gt;(maybecall :a #'mother #'father #'father #'brother) =&gt; nil&lt;br /&gt;;; и даже на brother не ругается, потому что до него дело не доходит&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Впрочем, я понял, что обычный AND (или AND-IT) в CL &amp;mdash; это и есть полноценная реализация Maybe стратегии. Притом простая и понятная, нет? &lt;br /&gt;&lt;br /&gt;3. Нужны ли монады за пределами Haskell'я?&lt;br /&gt;То, что в других языках представляет из себя единую ткань вычислений, в рамках Haskell разделено на 2 класса: чистые (обычные функции) и "грязные" (монады). Это приводит к разделению (дублированию) синтаксиса и усложнению программного кода (полная противоположность &lt;a href="http://letoverlambda.com/"&gt;дуалистического подхода&lt;/a&gt;). В то же время, это делает рассуждения о программах более структурированными и помогает (однако не гарантирует!) соблюдению принципа ссылочной целостности. В теории концепция монад полезна для понимания того, что такое вычисления. На практике же введение дополнительных правил приводит к тому, что появляется определенный предел выразительности языка и близости его к предметной области. Именно так, несмотря на заявления программистов на Haskell об обратном: концепция монады, монадного трансформера и т.д. неимоверно далеки от любой предметной области, которая встречается при программировании, а абстрагировать их средствами Haskell не удастся. Более того, само понятие вычисления, которое в других языках остается имплицитным, в Haskell'е достается на поверхность. Именно с этим, по-моему, связанны частые претензии к преувеличенной сложности Haskell программ.&lt;br /&gt;&lt;br /&gt;---&lt;br /&gt;&lt;br /&gt;В следующей записи я дам ответ на вопрос про самую главную монаду, а также попробую разобрать по-отдельности несколько других монад.&lt;br /&gt;&lt;br /&gt;Еще по теме:&lt;br /&gt;* &lt;a href="http://www.haskell.org/all_about_monads/html/index.html"&gt;All About Monads&lt;/a&gt;&lt;br /&gt;* &lt;a href="http://marijn.haverbeke.nl/monad.html"&gt;Why monads have not taken the Common Lisp world by storm&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-7020765686577939431?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/7020765686577939431/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=7020765686577939431' title='33 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/7020765686577939431'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/7020765686577939431'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/11/blog-post.html' title='О страшных монадах'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>33</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-8740739445670638242</id><published>2009-11-02T21:25:00.008+02:00</published><updated>2009-11-03T19:25:28.861+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>О Haskell</title><content type='html'>&lt;span style="font-size:-1;"&gt;Обновление: обсуждение на habrahabr.ru &lt;a href="http://habrahabr.ru/blogs/Haskell/74164/"&gt;Изучай Haskell ради... Haskell'а&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Я долго (несколько лет) не решался составить окончательное мнение о Haskell'e: слишком противоречивы были мысли. И вот, наконец, благодаря этой записи о &lt;a href="http://lionet.livejournal.com/44305.html"&gt;разборе программки определения двудольности графа&lt;/a&gt; я могу это сделать :)&lt;br /&gt;&lt;br /&gt;Я понял, что Haskell-программисты — в основном, нужно сказать, хобби-программисты — это те, кто программирует не решение задачи, алгоритм, систему, а Haskell! &lt;a href="http://www.blogger.com/post-create.g?blogID=6031647961506005424#h1"&gt;[1]&lt;/a&gt; Посмотрите, какой простой алгоритм описан в заметке, а сколько вокруг него нагромождено языковых конструкций, объяснений и дискуссий. Чтоб доказать, что он очень простой, привожу пример кода на Lisp'е, который решает ту же задачу без никаких монадных трансформеров и т.п. (не обращайте внимание на большой docstring):&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;(defun test-bipartite (graph)&lt;br /&gt;"Test for bipartiteness a GRAPH, that is given as a list of pairs&lt;br /&gt;vertix - list of immediately connected vertices, like:&lt;br /&gt;&lt;br /&gt;'((0 . (1))     (1 . (0 2 8))&lt;br /&gt;(2 . (1 3))   (3 . (2 6))&lt;br /&gt;(4 . (5 7))   (5 . (4))&lt;br /&gt;(6 . (3))     (7 . (4 8))&lt;br /&gt;(8 . (1 7)))&lt;br /&gt;-- bipartite one&lt;br /&gt;&lt;br /&gt;'((0 . (1 2))   (1 . (0 2 8))&lt;br /&gt;(2 . (1 3))   (3 . (2 6))&lt;br /&gt;(4 . (5 7))   (5 . (4))&lt;br /&gt;(6 . (3))     (7 . (4 8))&lt;br /&gt;(8 . (1 7)))&lt;br /&gt;-- not bipartite one&lt;br /&gt;&lt;br /&gt;'((0 . (1 2))   (1 . (0 2 8))&lt;br /&gt;(2 . (1 3))   (3 . (2 6))&lt;br /&gt;(4 . (5 7))   (5 . (4))&lt;br /&gt;(6 . (3))     (7 . (4 8))&lt;br /&gt;(8 . (1 7))   (9 . ()))&lt;br /&gt;-- not connected one"&lt;br /&gt;&lt;br /&gt;(let ((map (make-hash-table))&lt;br /&gt;     (visited '()))&lt;br /&gt;&lt;br /&gt; (labels ((con1 (v)&lt;br /&gt;            "vertices immediately connected to V"&lt;br /&gt;            (tail (assoc v graph)))&lt;br /&gt;&lt;br /&gt;          (paint (v level)&lt;br /&gt;            "paint the graph from vertix V, return all&lt;br /&gt;           visited so far (in subsequent calls of PAINT) vertices"&lt;br /&gt;            (pushnew v visited)&lt;br /&gt;            (setf (gethash v map) level)&lt;br /&gt;            (mapc (lambda (v) (paint v (1+ level)))&lt;br /&gt;                  (remove-if (lambda (v) (member v visited))&lt;br /&gt;                             (con1 v)))&lt;br /&gt;            visited))&lt;br /&gt;&lt;br /&gt;   ;; first we paint and check, that all vertices are visited&lt;br /&gt;   (unless (set-exclusive-or (paint 1 0)&lt;br /&gt;                             (mapcar #'first graph))&lt;br /&gt;     (every (lambda (entry)&lt;br /&gt;              (every (lambda (v)&lt;br /&gt;                       (oddp (+ (gethash (first entry) map)&lt;br /&gt;                                (gethash v map))))&lt;br /&gt;                     (tail entry)))&lt;br /&gt;            graph)))))&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Мне хорошо знакомо это умонастроение — когда в погоне за максимальным использованием мощи языка забываешь о самой задаче,— поскольку в Lisp-мире оно тоже часто встречается: есть языки, которые способны действительно увлечь. И выражение "это взорвало мне мозг" часто звучат и по поводу Lisp'а, и по поводу Haskell'а. Но это же — фигня! Конечно, не может не радовать узнать что-то новое, но не нужно же радоваться этому, как ребенок новой игрушке. Хороший язык программирования должен быть максимально понятен и прост, должен давать человеку свободу самовыражения. Честно говоря, именно этому я обрадовался, когда открыл для себя Lisp: что нашел то, что искал. А не тому, что увидел какую-то конструкцию или изворот, который не доводилось встречать раньше.&lt;br /&gt;&lt;br /&gt;Так что же, вывод: всем программировать на Lisp? Я, конечно, за, но вывод тут другой: Haskell — очень интересный язык, у которого есть как плюсы, так и минусы. Плюсы: это интересная семантика и сильная теоретическая база, хорошая скорость выполнения современных компиляторов. Минусы: ужасно нерегулярный синтаксис &lt;a href="http://www.blogger.com/post-create.g?blogID=6031647961506005424#h2"&gt;[2]&lt;/a&gt;, искусственная ограниченность, которая приводит к необходимости задействовать сложные подоходы там, где отлично справятся и простые. И им просто обязательно стоит заниматься, если вас интересует тема языков программирования как таковых, их развития и исследований. Из Haskell берут многое другие более практичные языки: яркий пример тому Clojure. &lt;b&gt;Но он не для написания больших систем и даже не для исследования алгоритмов в общем случае.&lt;/b&gt; У языков программирования кроме синтаксиса и семантики есть еще третий аспект, пожалуй даже важнейший, о котором часто забывают — прагматика. То, как язык используется, для чего он предназначается, чем живет сообщество его разработчиков и пользователей. &lt;b&gt;Прагматика Haskell'а заключается в том, что он существует прежде всго для исследования... Haskell'а&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a name="h1"&gt;[1]&lt;/a&gt; Есть, конечно, исключительные, прекраснейшие Haskell-программисты, написавшие на нем много полезного кода для реального мира, но это, как говорится в нелюбимом мной афоризме, только подтверждает правило.&lt;br /&gt;&lt;br /&gt;&lt;a name="h2"&gt;[2]&lt;/a&gt; Для современного языка нерегулярный синтаксис — это неуважение к своим пользователям. Ведь никто в современном мультиязыковом мире не программирует на одном языке, поэтому нельзя требовать от человека держать в голове идиосинкразии каждого. И этих общеупотребимых языков будет все больше и больше, а количество legacy кода уменьшаться не будет. Я сейчас имею дело с Lisp, Python, Php, C, JavaScript, Shell, Java. И это ведь не самый яркий пример.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-8740739445670638242?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/8740739445670638242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=8740739445670638242' title='35 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8740739445670638242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8740739445670638242'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/11/haskell.html' title='О Haskell'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>35</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-9009956875772299009</id><published>2009-08-03T14:40:00.002+03:00</published><updated>2009-08-03T14:44:26.568+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Очередная встреча по Lisp</title><content type='html'>&lt;a href="http://www.developers.org.ua/calendar/event/531khsfol4dujub66mkc4cet74/ "&gt;Первая встреча по Lisp'у&lt;/a&gt;, я считаю, удалась.&lt;br /&gt;Попробуем еще раз: &lt;a href="http://www.developers.org.ua/calendar/event/f2hv5cnvjg2uj3qqtk43r2fv7s/"&gt;http://www.developers.org.ua/calendar/event/f2hv5cnvjg2uj3qqtk43r2fv7s/&lt;/a&gt;.&lt;br /&gt;Тема: кодогенерация.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-9009956875772299009?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/9009956875772299009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=9009956875772299009' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/9009956875772299009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/9009956875772299009'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/08/lisp.html' title='Очередная встреча по Lisp'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-2466762185476753143</id><published>2009-06-12T14:22:00.001+03:00</published><updated>2009-06-12T14:24:26.378+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>О микроплатежах</title><content type='html'>&lt;b&gt;Написано для:&lt;/b&gt; &lt;a href="http://blog.liga.net/user/vdyomkin/article/2733.aspx"&gt;Лига.Блоги&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;В своем &lt;a href="http://blog.liga.net/user/vdyomkin/article/2727.aspx"&gt;предыдущем комментарии&lt;/a&gt; я затронул тему того, что до сих пор ни в Украине, ни в мире не появился удобный и получивший широкое распространение сервис микроплатежей. Вообще, под микроплатежами можно понимать разные вещи. Я имею в виду платежи, которые незначительны настолько, что человеку проще перелатить даже до 10-20% от их суммы, зато произвести их быстро и без лишних забот. Понятно, что для каждого это разные суммы, но в среднем это платежи, комиссия по которым не превышает пары десятков гривен (для нашей страны в данное время). В общем, современная экономика говорит, что главная проблема — это транзакционные издержки и их нужно стараться минимизировать. Эти издержки складываются из финансовых, временных, а также усилий и рисков. Микроплатежи находятся в той части спектра экономической активности, где увеличение финансовых издержек наименее нежелательное из перечисленных пунктов.&lt;br /&gt;&lt;br /&gt;Еще раз, зачем нужны микроплатежи?&lt;br /&gt;&lt;br /&gt;    * Для оплаты товара в Интернет-магазинах. Иногда да, хотя в данном случае, поскольку имеет место акт передачи товара из рук в руки, можно применять оплату наличными (как, собственно, у нас в основном и делаеют). К сожалению, как в анекдоте, что заказывать товары при коммунизме можно будет по телефону, а получать по телевизору — у нас пока не получается. Микроплатежи отлично могли бы применяться при доставке товаров по почте, но таких товаров у нас, традиционно, не много.&lt;br /&gt;    * Для переводов небольших сумм между людьми. Однозначно, это намного проще, чем банковские или мгновенные переводы. И они в этой сфере уже применяются, но пока по-серому.&lt;br /&gt;    * Для оплаты различных услуг: коммунальных, виртуальных и некоторых реальных. Это основное применение для микроплатежей. На самом деле, как раз отсутствие такой полноценной возможности тормозит развитие очень большого числа мелких услуг (в первую очередь виртуальных).&lt;br /&gt;&lt;br /&gt;На мой взгляд, единственный путь появления полноценных микроплатежей — через операторов мобильной связи. По сути дела, сейчас они предоставляют такую возможность, но только в ограниченной форме: пополнение счета другого человка (*125*...) И этой возможностью некоторые уже сейчас пользуются даже для организации оплаты мелих услуг. Это типичные мироплатежи, в которых комиссия составляет до 25% (и не смотря на такой высокий процент, люди пользуеются этой услугой).&lt;br /&gt;В чем ограниченность? Главное, нельзя использовать переведенные деньги для оплаты чего-то иного, кроме услуг самого оператора и его афиллированных компаний, нельзя, что называется, "вывести деньги из системы". Второй, менее значительный недостаток: сейчас суммы платежей должны быть кратны 10 (а ведь нет никаких проблем снизить кратность до 1).&lt;br /&gt;&lt;br /&gt;Осталось сделать последний шаг: упразднивший эти недостатки. И тогда каждый человек с мобильным телефоном сможет без помощи банков переводить деньги другому. Каждый предприниматель получит удобный и надежный способ получать оплату за свои услуги. Оператор, сделавший такой шаг первым, сможет переманить к себе большое количество абонентов, это не говоря о комиссионных доходах, которые он будет получать от работы этой системы.&lt;br /&gt;&lt;br /&gt;Так почему же этого не делается? Загвоздки:&lt;br /&gt;&lt;br /&gt;    * Юридические. При этом оператор становится банком? Не совсем. Всего лишь расчетной системой. Скорее всего подобная деятельность требует лицензирования и согласования. Однако операторам и так приходится заниматься подобными вещами постоянно. Использование системы для ухода от налогов? Во-первых, не больше, чем при расчете наличными :) Во-вторых, есть пути мимимизации этого: например, обязательная регистрация для мерчантов (тех, кто хочет использовать систему для получения оплаты за товары или услуги). А так ведь, всё прозрачно — наоборот удобно для налоговой.&lt;br /&gt;    * Издержки при вводе денег в систему: условно говоря, человек покупает карточку предоплаты на 30 гривен, выпуск и распространение которой стоит 1 грн. Эта гривна — это 3%, которые не сможет компенсировать комиссия за вывод денег из системы (которая тут должна быть не больше тех же 2-3%). Но ее можно компенсировать введя комиссию на каждый платеж: ту же гривну или даже 50 копеек.&lt;br /&gt;    * Мошенничество. Для микроплатежей этот вопрос стоит на последнем месте. Ведь никого не смущают возможности мошенничества при пополнении чужого мобильного счета: такая функция намного полезнее, так ведь?&lt;br /&gt;    * Организационные. Безусловно, построение подобной системы — это серьезный проект. Однако, он менее серьезен, чем построение самой сети оператора, с чем они успешно справляется.&lt;br /&gt;&lt;br /&gt;В общем, как по мне, неразрешимых проблем здесь нет: все упирается лишь в наличие видения и желания у руководства любого из общенациональных мобильных операторов. Микроплатежи — это естественная смежная область, развитие бизнеса в которую для мобильных телекомов дало бы толчек и их развитию в целом, и было бы несомненно полезно для общества.&lt;br /&gt;&lt;br /&gt;А в перспективе подобная система может стать вообще глобальной и составить конкуренцию таким фирмам как Visa и MasterCard.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-2466762185476753143?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/2466762185476753143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=2466762185476753143' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/2466762185476753143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/2466762185476753143'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/06/blog-post.html' title='О микроплатежах'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-814716416582694535</id><published>2009-06-02T00:51:00.002+03:00</published><updated>2009-06-02T00:55:33.290+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>k-nucleotide benchmark</title><content type='html'>Read the discussion on k-nucleotide benchmark in debian shootout (http://groups.google.com/group/comp.lang.lisp/msg/d50c86053c9000b7) and decided to play with it a little: to use as hash-table keys numbers in base 4 instead of gene strings. That gave a speed gain of around 30%: http://shootout.alioth.debian.org/u32q/benchmark.php?test=knucleotide&amp;lang=sbcl&amp;id=3.&lt;br /&gt;But still 12 times slower, than the C++ variant. Perhaps native strings (instead of full unicode) would have given 2-2.5 times additional speedup. But what else can be improved?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-814716416582694535?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/814716416582694535/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=814716416582694535' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/814716416582694535'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/814716416582694535'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/06/k-nucleotide-benchmark_02.html' title='k-nucleotide benchmark'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-8594255732436376194</id><published>2009-06-02T00:40:00.006+03:00</published><updated>2009-06-02T00:58:28.043+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>k-nucleotide бенчмарка</title><content type='html'>Почитал дискуссию о k-nucleotide бенчмарке в debian shootout'е (&lt;a href="http://groups.google.com/group/comp.lang.lisp/msg/d50c86053c9000b7"&gt;в c.l.l.&lt;/a&gt;) и решил немного с ней поиграться: вместо строк генов в качестве ключей хеш-таблицы использовать соответствующие числа в базе 4. В результате удалось улучшить время выполнения примерно на 30%: &lt;a href="http://shootout.alioth.debian.org/u32q/benchmark.php?test=knucleotide&amp;lang=sbcl&amp;id=3"&gt;http://shootout.alioth.debian.org/u32q/benchmark.php?test=knucleotide&amp;lang=sbcl&amp;id=3&lt;/a&gt;.&lt;br /&gt;Но все равно медленнее С++ аналога в 12 раз. Допустим, если отнять unicode-строки, то получится ускорение еще в 2-2,5 раза. А что еще можно улучшить?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-8594255732436376194?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/8594255732436376194/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=8594255732436376194' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8594255732436376194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8594255732436376194'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/06/k-nucleotide-benchmark.html' title='k-nucleotide бенчмарка'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-6049558139890089108</id><published>2009-05-27T14:04:00.001+03:00</published><updated>2009-05-27T14:05:48.657+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Идеи по поводу tedxkyiv</title><content type='html'>@tagline&lt;br /&gt;У оригинального TED обычно есть какие-то большие темы (по 3-4 на конференцию), например: "зеленое будущее", "переосмысление бедности", "сознание" и т.д. Наверно, было бы неплохо, чтобы и у TEDx тоже была какая-то объединяющая тема. И посколько в последнее время Украина как-то не дает таланты, являющиеся глобальными лидерами мысли (у нас сейчас хорошо получаются только футболисты и боксеры), то и тему можно выбрать только локальую, украинскую. Мне сразу приходит на ум "Как нам модернизировать Украину?"&lt;br /&gt;&lt;br /&gt;@speakers&lt;br /&gt;Ну и, в соотвествии с темой должны подбираться докладчики. Я так понимаю, что формула TED: известная личность + видение. В этом плане у нас тоже не густо: во всяком случае трудно выделить среди известных политиков, бизнесменов, ученых или гиков тех, у кого действительно есть видение, которое бы было шире их личных интересов и касалось хотя бы всей Украины, не говоря уже о мире в целом.&lt;br /&gt;&lt;br /&gt;Единственный хороший пример, который мне приходит на ум -- это Татьяна Монтян. Она известный человек с вполне конкретными достижениями (была адвокатом в нескольких резонансных делах, создала блог "Инфопорн", признанный лучшим коллективным блогом Украины на BlogCampCEE'08), и у нее есть видение: от коррупции нас спасет введение всеобщих открытых реестров недвижимого имущества и переход к титульной системе учета прав на него.&lt;br /&gt;&lt;br /&gt;Другие известные люди, теоретически могущие иметь видение, которые приходят на ум:&lt;br /&gt;* Вячеслав Брюховецкий, бывший ректор Киево-могилянской академии&lt;br /&gt;* Михаил Згуровский, ректор КПИ&lt;br /&gt;* Михайло Свистович (создатель сайта maidan.org.ua и один из инициаторов Гражданской кампании "Пора") и его жена Мирослава, бывшая мером Ирпеня&lt;br /&gt;* из бизнесменов можно упомянуть разве что Романа Хмиля, директора компании "GlobalLogic"&lt;br /&gt;&lt;br /&gt;Наверное, есть смысл устроить брейншторм на тему докладчиков, потому что кандидатур (во всяком случае, на первый взгляд) совсем не много.&lt;br /&gt;&lt;br /&gt;Также в рамках этой темы можно было бы пригласить выступить кого-то из украинцев, добившихся выдающихся результатов за рубежом, чтобы он мог рассказать о видении нашей страны оттуда. Очень кстати также было бы выступление кого-то из иностранцев, работающих у нас. Например, на ум приходит Нико Ланге, директор фонда Конрада Аденауэра в Украине, запомнившийся своими откровенными статьями в Украинской Правде.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-6049558139890089108?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/6049558139890089108/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=6049558139890089108' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6049558139890089108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6049558139890089108'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/05/tedxkyiv.html' title='Идеи по поводу tedxkyiv'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-5789140114088282247</id><published>2009-05-25T17:45:00.001+03:00</published><updated>2009-05-25T17:46:59.254+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='etc'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Встреча по Lisp'у</title><content type='html'>Устраиваю в эту пятницу встречу пользователей Lisp в Киеве.&lt;br /&gt;Подробности: http://www.developers.org.ua/calendar/event/531khsfol4dujub66mkc4cet74/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-5789140114088282247?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/5789140114088282247/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=5789140114088282247' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5789140114088282247'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5789140114088282247'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/05/lisp.html' title='Встреча по Lisp&apos;у'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-5987822961194046150</id><published>2009-05-25T12:42:00.002+03:00</published><updated>2009-05-25T12:46:20.199+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Мыслекарты для планирования трат</title><content type='html'>&lt;b&gt;Написано для&lt;/b&gt;: &lt;a href="http://habrahabr.ru/blogs/ui_design_and_usability/60388/"&gt;habrahabr.ru&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Инструмент &lt;a href="http://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D1%80%D1%82%D1%8B_%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D0%B8"&gt;Mind Map&lt;/a&gt; довольно нов: настолько, что у него нет даже устоявшегося русского названия. Обычно его называют интеллект-картами, картами памяти, сознания или мозга, и даже ментальными картами. Как по мне, довольно громоздкие и не удобные имена, в которых, что самое главное, не четко передается суть понятия (мне в них слышатся какие-то анатомические оттенки: вот тут левое полушарие, тут правое, а тут мозжечок… :) А ведь это, если максимально упростить, картинки, изображащие понятия и логические связи между ними. Можно было бы сказать карты понятий, но тоже звучит громоздко, а вот карты мыслей — проще. Ну и, следуя по традиции словообразования в русском языке: мыслекарты![1]&lt;br /&gt;&lt;br /&gt;Мыслекарты помогают лучше осознать какие-то сложные и/или состоящие из многих компонентов системы за счет подключения визуального канала восприятия и, соответственно, образного мышления. Говорят, что в среднем человек может удерживать в оперативной памяти лишь 7 отдельных объектов. Но это в том случае, если эти объекты слабо связаны в сознании друг с другом. А вот, если добавить ассоциации, то потенциальные возможности работы с ними в уме и запоминания неимоверно расширяются. В книге &lt;a href="http://www.pianofundamentals.com/"&gt;"Основы практики на пианино"&lt;/a&gt; утверждается, что именно на этом построены методы людей, практикующих фотографическую память...&lt;br /&gt;&lt;br /&gt;Но я отвлекся. При чем мыслекарты к личным финансам? Не считая, конечно, того, что оба эти явления как-то касаются темы личного развития :), связь нам подсказал один из пользователей нашего сервиса по учету финансов[2]. А что если применить эти карты к планированию трат на ближайшее будущее? Например, можно задумать траты, которые могут потенциально выйти за рамки семейного бюджета, но чтобы это понять, нужно посидеть с карандашом и блокнотом и прикинуть свои будущие расходы и доходы, добавить ингридиенты сбережений и незапланированных трат, и щепотку везения, а на выходе получить прогноз, удастся ли осуществить запланированное. В общем-то, в этом может неплохо помочь учетная система: ведь в ней есть уже почти вся нужная информация. Другой вопрос, что ее нужно показать так, чтобы человек не утонул в этом океане цифр. Вот тут на помощь и приходит наглядность и простота мыслекарт. Но, как говорится, «лучше один раз увидеть, чем сто раз услышать», поэтому попробую показать, что имеется в виду.&lt;br /&gt;&lt;br /&gt;Вот так выглядел оригинальный набросок, присланный мне:&lt;br /&gt;&lt;img src="http://img.photobucket.com/albums/v473/pufpuf/mind-map-sketch.jpg" alt="набросок мыслекарты"/&gt;&lt;br /&gt;&lt;br /&gt;А вот такой вышла наша первая реализация:&lt;br /&gt;&lt;img src="http://img.photobucket.com/albums/v473/pufpuf/mindmap.png" alt="мыслекарта"/&gt;&lt;br /&gt;&lt;br /&gt;Попробую кратко описать ее работу. Сейчас можно планировать во временных горизонтах месяца, квартала и года. Для пользователя за предыдущий период, заканчивающийся сегодняшним днем, подсчитываются его траты и доходы. Эти суммы и есть прогноз на период планирования. На основании этого прогноза, а также баланса на текущий момент считается прогноз баланса в конце срока, а разница между планируемым доходом и тратами — это накопленный/потраченный остаток конкретно за период. Поскольку наши карты служат прежде всего для планирования трат[3], то траты разбиваются по нескольким самым большим категорям. Человек может прикинуть, по каким из них что-то изменится (например, можно сэкономить) и внести эти изменения прямо на карту. Какие-то категории можно вообще убрать. (Кроме того, можно перейти по ссылке и посмотреть на все траты по каждой категории). Естественно, все изменения мгновенно отражаются на суммах расхода, остатка и баланса. Также можно поменять и ожидаемый доход. Человек может добавить на карту свои планы и сразу увидеть, насколько остатка денег за период хватит, чтобы покрыть каждый из них в отдельности, а также их общую сумму. Наконец, определившись с какими-то из планов, их можно «добавить в корзину» (подтвердить). И только тогда они внесут свою лепту в сумму расходов за период. Собственно, это пока и все.&lt;br /&gt;&lt;br /&gt;Стоит отметить, что в данном случае у нас получается не совсем классическая мыслекарта: пока что мы не реализовали показа по желанию пользователя подкатегорий, хотя это, наверно, и не принципиально. Кажется, в этой информации нет большой ценности, а вот сделать карту перегруженной она вполне способна.&lt;br /&gt;&lt;br /&gt;Что касается технической стороны, то это тема для статьи в другом блоге. Замечу лишь, что карта полностью реализована на языке Javascript, подручным материалом выступала незаменимая библиотека jQuery (беру &lt;a href="http://twitter.com/vseloved/status/1024324890"&gt;свои слова&lt;/a&gt; назад: это не костыль :). Конечно, большим недостатком использования исключительно DOM является отсутствие возможности изображать произвольные объекты: наверно, еще пару лет придется пользоваться различными хаками, пока не станет повсеместно распространенным тег CANVAS. В данном случае, например, вместо рисования линий показываются масштабируемые готовые картинки. Это позволяет добиться сравнительно небольшой нагрузки на браузер, однако иногда приводит к мини подвисаниям (в момент переключения между картинками).&lt;br /&gt;&lt;br /&gt;В общем, не знаю, конечно, станет ли эта функция действительно полезным инструментом для управления своим бюджетом или же останется лишь красивой игрушкой. Надеюсь увидеть примеры первого. Для меня она стала интересным экспериментом в визуализации финансовой информации. Возможно, этот метод применим и в других финансовых сферах, например, в системах управленческой отчетности предприятия или программах для биржевых трейдеров. А что вы думаете?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;[0] &lt;a href="http://community.livejournal.com/mindmaps/"&gt;Сообщество LiveJournal по теме мыслекарт&lt;/a&gt;&lt;br /&gt;[1] А еще, выражаясь научным языком, наверно, их можно отнести к семантическим сетям&lt;br /&gt;[2] Приятно, что в этом отношении сбывается то, о чем я написал в &lt;a href="http://habrahabr.ru/blogs/startup/47250/"&gt;анонсе fin-ack.com&lt;/a&gt;: многие интересные идеи приходят от пользователей&lt;br /&gt;[3] Впрочем, ничто не мешает использовать их для планирования доходов или еще чего-то&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-5987822961194046150?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/5987822961194046150/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=5987822961194046150' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5987822961194046150'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5987822961194046150'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/05/blog-post.html' title='Мыслекарты для планирования трат'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-4882924491383600428</id><published>2009-04-11T17:42:00.004+03:00</published><updated>2009-04-11T18:22:39.769+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>42</title><content type='html'>Кстати, об основополагающем вопросе... Это число, наверно, следующее по количеству упоминаний после фразы "Hello world" и foo-bar-baz в различных тюториалах по программированию. Когда-то я перевел отрывок из книги, в котором рассказывается о его истории, на русский. &lt;a href="http://pufpuf.livejournal.com/12323.html"&gt;Вот от&lt;/a&gt;.&lt;br /&gt;В целом, Дугласа Адамса, наверно, стоит почитать каждому -- в нем столько злободневного и в то же время вечного...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-4882924491383600428?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/4882924491383600428/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=4882924491383600428' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/4882924491383600428'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/4882924491383600428'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/04/42.html' title='42'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-3915986647933648395</id><published>2009-04-05T21:20:00.004+03:00</published><updated>2009-04-09T12:01:24.750+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>correcting Transfer-encoding:chunked problem in nginx</title><content type='html'>While Igor Sysoev is thinking on how to properly (i.e. robustly and efficiently) implement handling of Content-encoding:chunked with unset Content-Length in nginx, I needed to hack a quick workaround (for those, who need it "here and now"). I decided to post it here for those who may need it:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;--- old/ngx_http_request.c 2009-04-05 20:41:18.000000000 +0300&lt;br /&gt;+++ new/ngx_http_request.c 2009-04-05 20:38:33.000000000 +0300&lt;br /&gt;@@ -1403,16 +1403,6 @@&lt;br /&gt;         return NGX_ERROR;&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;-    if (r-&gt;headers_in.transfer_encoding&lt;br /&gt;-        &amp;&amp; ngx_strcasestrn(r-&gt;headers_in.transfer_encoding-&gt;value.data,&lt;br /&gt;-                           "chunked", 7 - 1))&lt;br /&gt;-    {&lt;br /&gt;-        ngx_log_error(NGX_LOG_INFO, r-&gt;connection-&gt;log, 0,&lt;br /&gt;-                      "client sent \"Transfer-Encoding: chunked\" header");&lt;br /&gt;-        ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);&lt;br /&gt;-        return NGX_ERROR;&lt;br /&gt;-    }&lt;br /&gt;-&lt;br /&gt;     if (r-&gt;headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {&lt;br /&gt;         if (r-&gt;headers_in.keep_alive) {&lt;br /&gt;             r-&gt;headers_in.keep_alive_n =&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;--- old/ngx_http_request_body.c 2009-04-05 20:41:39.000000000 +0300&lt;br /&gt;+++ new/ngx_http_request_body.c 2009-04-05 20:34:45.000000000 +0300&lt;br /&gt;@@ -54,11 +54,6 @@&lt;br /&gt; &lt;br /&gt;     r-&gt;request_body = rb;&lt;br /&gt; &lt;br /&gt;-    if (r-&gt;headers_in.content_length_n &lt; 0) {&lt;br /&gt;-        post_handler(r);&lt;br /&gt;-        return NGX_OK;&lt;br /&gt;-    }&lt;br /&gt;-&lt;br /&gt;     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);&lt;br /&gt; &lt;br /&gt;     if (r-&gt;headers_in.content_length_n == 0) {&lt;br /&gt;@@ -180,7 +175,8 @@&lt;br /&gt; &lt;br /&gt;     } else {&lt;br /&gt;         b = NULL;&lt;br /&gt;-        rb-&gt;rest = r-&gt;headers_in.content_length_n;&lt;br /&gt;+        rb-&gt;rest = r-&gt;headers_in.content_length_n &gt;= 0 ? r-&gt;headers_in.content_length_n&lt;br /&gt;+                                                       : NGX_MAX_SIZE_T_VALUE;&lt;br /&gt;         next = &amp;rb-&gt;bufs;&lt;br /&gt;     }&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;In the mean time I've sorted out for myself the server's internals. What can I say? First of all the event-driven architecture is complicated. Especially, when there's no high-level description, who is calling what. And especially under virtually complete absence of comments in the code.&lt;br /&gt;&lt;br /&gt;And about C and Lisp (based on examining the source of the two HTTP-servers: nginx and Hunchentoot). Lisp is clay, shape whatever you want with all the connected advantages and shortcomings. C... There's a children's construction set from metal bars and corners. It's fun to play with them. And, generally, everything is quite clear, it's possible to assemble good, solid constructions. But making a vase with rounded corners &amp;mdash; it's for the geeks...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-3915986647933648395?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/3915986647933648395/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=3915986647933648395' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3915986647933648395'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3915986647933648395'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/04/correcting-content-encodingchunked.html' title='correcting Transfer-encoding:chunked problem in nginx'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-8881661043988076152</id><published>2009-04-05T20:53:00.005+03:00</published><updated>2009-04-09T12:17:39.762+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>исправляем проблему с Transfer-encoding:chunked в nginx</title><content type='html'>Пока Игорь Сысоев думает над тем, как правильно (т.е. надежно и быстродейственно) реализовать обработку Content-encoding:chunked при не заданной Content-Length в nginx, мне пришлось сделать workaround (для тех, кому это нужно "здесь и сейчас"). Решил его выложить, может, кому-то кроме меня это тоже будет нужно:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;--- old/ngx_http_request.c 2009-04-05 20:41:18.000000000 +0300&lt;br /&gt;+++ new/ngx_http_request.c 2009-04-05 20:38:33.000000000 +0300&lt;br /&gt;@@ -1403,16 +1403,6 @@&lt;br /&gt;         return NGX_ERROR;&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;-    if (r-&gt;headers_in.transfer_encoding&lt;br /&gt;-        &amp;&amp; ngx_strcasestrn(r-&gt;headers_in.transfer_encoding-&gt;value.data,&lt;br /&gt;-                           "chunked", 7 - 1))&lt;br /&gt;-    {&lt;br /&gt;-        ngx_log_error(NGX_LOG_INFO, r-&gt;connection-&gt;log, 0,&lt;br /&gt;-                      "client sent \"Transfer-Encoding: chunked\" header");&lt;br /&gt;-        ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);&lt;br /&gt;-        return NGX_ERROR;&lt;br /&gt;-    }&lt;br /&gt;-&lt;br /&gt;     if (r-&gt;headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {&lt;br /&gt;         if (r-&gt;headers_in.keep_alive) {&lt;br /&gt;             r-&gt;headers_in.keep_alive_n =&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;--- old/ngx_http_request_body.c 2009-04-05 20:41:39.000000000 +0300&lt;br /&gt;+++ new/ngx_http_request_body.c 2009-04-05 20:34:45.000000000 +0300&lt;br /&gt;@@ -54,11 +54,6 @@&lt;br /&gt; &lt;br /&gt;     r-&gt;request_body = rb;&lt;br /&gt; &lt;br /&gt;-    if (r-&gt;headers_in.content_length_n &lt; 0) {&lt;br /&gt;-        post_handler(r);&lt;br /&gt;-        return NGX_OK;&lt;br /&gt;-    }&lt;br /&gt;-&lt;br /&gt;     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);&lt;br /&gt; &lt;br /&gt;     if (r-&gt;headers_in.content_length_n == 0) {&lt;br /&gt;@@ -180,7 +175,8 @@&lt;br /&gt; &lt;br /&gt;     } else {&lt;br /&gt;         b = NULL;&lt;br /&gt;-        rb-&gt;rest = r-&gt;headers_in.content_length_n;&lt;br /&gt;+        rb-&gt;rest = r-&gt;headers_in.content_length_n &gt;= 0 ? r-&gt;headers_in.content_length_n&lt;br /&gt;+                                                       : NGX_MAX_SIZE_T_VALUE;&lt;br /&gt;         next = &amp;rb-&gt;bufs;&lt;br /&gt;     }&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Мимоходом, разобрался немного во внутренностях этого сервера. Что можно сказать? Во-первых, event-driven архитектура &amp;mdash; это сложно. Особенно, когда нет общего описания, кто кого вызывает. И особенно при почти полном отсутствии комментариев в коде.&lt;br /&gt;&lt;br /&gt;Ну и про С и Lisp (по итогам копания в коде двух HTTP-серверов: nginx и Hunchentoot). Lisp &amp;mdash; это глина, лепи себе, что заблагорассудится, со всеми вытекающими плюсами и минусами. C... Есть такие конструторы из металлических планок и уголков. Интересно с ними играться. И, в общем-то, довольно понятно все, можно собрать хорошие, надежные конструкции. Но вот сделать из этого какую-нибудь вазу с закругленными углами &amp;mdash; это для фанатов...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-8881661043988076152?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/8881661043988076152/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=8881661043988076152' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8881661043988076152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8881661043988076152'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/04/content-encodingchunked-nginx.html' title='исправляем проблему с Transfer-encoding:chunked в nginx'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-1206805284531530561</id><published>2009-03-23T15:32:00.006+02:00</published><updated>2009-06-17T20:13:52.174+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>CL-WHO macros</title><content type='html'>CL-WHO is a library, that performs HTML generation from s-expressions (which I'll call &lt;b&gt;pseudo-html forms&lt;/b&gt;). As for now it has one major shortcoming due to the way the input is processed by the WITH-HTML-OUTPUT macro: it doesn't support using macros, that expand to pseudo-html forms. (And the reason for it is that the input is first subjected to rule-based transformations to allow intermixing pseudo-html and lisp forms).&lt;br /&gt;&lt;br /&gt;Let's look at the simple example...&lt;br /&gt;&lt;br /&gt;First we just process plain pseudo-html:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;CL-USER&gt; (with-html-output-to-string (s)&lt;br /&gt;           (:p "a"))&lt;br /&gt;"&amp;lt;p&amp;gt;a&amp;lt;/p&amp;gt;"&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Then we mix it with Lisp code with the help of using a special symbol HTM to hint WITH-HTML-OUTPUT, where to switch:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;CL-USER&gt; (with-html-output-to-string (s)&lt;br /&gt;           (:ul (dolist (a '(1 2 3))&lt;br /&gt;                  (htm (:li (str a))))))&lt;br /&gt;"&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;"&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Now we try to define a macro:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;CL-USER&gt; (defmacro test (a)&lt;br /&gt;           `(htm (:li (str ,a))))&lt;br /&gt;TEST&lt;br /&gt;CL-USER&gt; (with-html-output-to-string (s)&lt;br /&gt;           (:ul (dolist (a '(1 2 3))&lt;br /&gt;                  (test a))))&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;But the following code will cause UNDEFINED-FUNCTION error ("The function :LI is undefined.") Why? Because the translation of pseudo-html s-expressions would be already finished by the time of macroexpansion of TEST, so it will be expanded not inside &lt;code&gt;(:ul (dolist (a '(1 2 3)) ...)&lt;/code&gt;, but inside calls to WRITE-STRING.&lt;br /&gt;&lt;br /&gt;It's definitely unlispy (not to be able to use macros anywhere you want :), so from the beginning of using CL-WHO I tried to find a work-around. (And I guess, many did the same). At least in the mailing list I turned out to be not the only one, who proposed something...&lt;br /&gt;&lt;br /&gt;Below 3 solutions, that I've tried (in chronological order) are described. Each implements a distinct approach to combining pseudo-html forms with macros.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;Delimited macroexpansion&lt;/b&gt;: introduce an additional special symbol (in the lines of already present HTM, STR, ESC and FMT) -- EMB, which will instruct CL-WHO to MACROEXPAND-1 the enclosed form during input transformation.&lt;br /&gt;&lt;br /&gt;This is the example (based on the example from CL-WHO manual):&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(defmacro embed-td (j)&lt;br /&gt;  `(:td :bgcolor (if (oddp ,j) "pink" "green")&lt;br /&gt;        (fmt "~@R" (1+ ,j))))&lt;br /&gt;&lt;br /&gt;(with-html-output-to-string (*http-stream*)&lt;br /&gt; (:table :border 0 :cellpadding 4&lt;br /&gt;         (loop for i below 25 by 5&lt;br /&gt;               do (htm&lt;br /&gt;                   (:tr :align "right"&lt;br /&gt;                        (loop for j from i below (+ i 5)&lt;br /&gt;                              do (htm (emb (embed-td j)))))))))&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I implemented it by tweaking the WITH-HTML-OUTPUT underlying input transforming function TREE-TO-TEMPLATE:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(defun tree-to-template (tree)&lt;br /&gt;  "Transforms an HTML tree into an intermediate format - mainly a&lt;br /&gt;flattened list of strings. Utility function used by TREE-TO-COMMANDS-AUX."&lt;br /&gt;  (loop for element in tree&lt;br /&gt;        nconc (cond ((or (keywordp element)&lt;br /&gt;                         (and (listp element)&lt;br /&gt;                              (keywordp (first element)))&lt;br /&gt;                         (and (listp element)&lt;br /&gt;                              (listp (first element))&lt;br /&gt;                              (keywordp (first (first element)))))&lt;br /&gt;                      ;; normal tag&lt;br /&gt;                      (process-tag element #'tree-to-template))&lt;br /&gt;                     ((listp element)&lt;br /&gt;                      ;; most likely a normal Lisp form (check if we&lt;br /&gt;                      ;; have nested HTM subtrees) or an EMB form&lt;br /&gt;                      (list&lt;br /&gt;                       (if (eql (car element) 'emb)&lt;br /&gt;                           (replace-htm (list 'htm (macroexpand-1 (cadr element)))&lt;br /&gt;                                        #'tree-to-template)&lt;br /&gt;                           (replace-htm element #'tree-to-template))))&lt;br /&gt;                     (t&lt;br /&gt;                      (if *indent*&lt;br /&gt;                        (list +newline+ (n-spaces *indent*) element)&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This is a simple and undertested solution, so it, probably, may fail in some corner cases. At least I couldn't make it reliably work, when I tried to combine it with PARENSCRIPT code (see http://common-lisp.net/pipermail/cl-who-devel/2008-July/000351.html). So it's just a direction (yet a quite possible one). But I decided to leave it aside to try the second approach...&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Preliminary macroexpansion&lt;/b&gt;: don't modify CL-WHO, but build on top of it a macro infrastructure to expand the macros, before the whole form is passed to WITH-HTML-OUTPUT.&lt;br /&gt;&lt;br /&gt;After thinking about a problem for some time I came to a conclusion, that it would be better to treat CL-WHO as just a good processor for static pseudo-html data, and on top of it build macros, which will allow eventually to add macroexpansion ability. For that let's consider such model: if we want to use macros, we define an "endpoint", at which they should be expanded (the function, that generates HTML after all). I called this endpoint WHO-PAGE. It will function as DEFMACRO, taking a backquote template, in which we can explicitly control evaluation of forms.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(defmacro def-who-page (name (&amp;optional stream) pseudo-html-form)&lt;br /&gt; "Creates a function to generate an HTML page with the use of&lt;br /&gt;WITH-HTML-OUTPUT macro, in which pseudo-html forms, constructed with&lt;br /&gt;DEF-WHO-MACRO, can be embedded. If STREAM is nil/not supplied&lt;br /&gt;WITH-HTML-OUTPUT-TO-STRING to a throwaway string will be used,&lt;br /&gt;otherwise -- WITH-HTML-OUTPUT"&lt;br /&gt; `(macrolet ((who-tmp ()&lt;br /&gt;               `(,@',(if stream&lt;br /&gt;                         `(with-html-output (,stream nil :prologue t))&lt;br /&gt;                         `(with-html-output-to-string&lt;br /&gt;                            (,(gensym) nil :prologue t)))&lt;br /&gt;                     ,,pseudo-html-form)))&lt;br /&gt;    (defun ,name ()&lt;br /&gt;      (who-tmp))))&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The definition of this HTML-generation functions uses macrolet to expand the macros before passing the body to CL-WHO. And the special macros, that will be used inside the template, could be defined like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(defmacro def-who-macro (name (&amp;rest args) pseudo-html-form)&lt;br /&gt;  "A macro for use with CL-WHO's WITH-HTML-OUTPUT."&lt;br /&gt;    `(defmacro ,name (,@args)&lt;br /&gt;     `'(htm ,,pseudo-html-form)))&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;But that's not all. Sometimes we will need to evaluate the arguments, passed to who-macro (to be able to accept pseudo-html forms as arguments), so a complimentary utility can be added (it's named by the PARENSCRIPT naming convention):&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(defmacro def-who-macro* (name (&amp;rest args) pseudo-html-form)&lt;br /&gt;  "Who-macro, which evaluates its arguments (like an ordinary function,&lt;br /&gt;which it is in fact.&lt;br /&gt;   Useful for defining syntactic constructs"&lt;br /&gt;  `(defun ,name (,@args)&lt;br /&gt;     ,pseudo-html-form))&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The example usage can be this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(def-who-macro title (for &amp;optional (show-howto? t))&lt;br /&gt;  "A title with a possibility to show howto-link by the side"&lt;br /&gt;  `(:div :class "title"&lt;br /&gt;         (:h2 :class "inline"&lt;br /&gt;              "&amp;rarr;&amp;nbsp;" ,(ie for)) "&amp;nbsp;"&lt;br /&gt;         (when ,show-howto?&lt;br /&gt;           (how-to-use ,for)) (:br)))&lt;br /&gt;&lt;br /&gt;(def-who-macro* user-page (title &amp;key head-form onload body-form)&lt;br /&gt;  `(:html&lt;br /&gt;    (:head (:title ,(ie title))&lt;br /&gt;    (:link :rel "stylesheet" :type "text/css"&lt;br /&gt;                  :href "/css/user.css")&lt;br /&gt;    (:script :src "/js/user.js" :type "text/javascript"&lt;br /&gt;                    "")&lt;br /&gt;    ,head-form)&lt;br /&gt;    (:body :onload ,onload&lt;br /&gt;    ,(header-box)&lt;br /&gt;    (:table ,@(wide-class)&lt;br /&gt;     ,body-form)&lt;br /&gt;    ,(footer-box))))&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I could successfully use this approach and have added some additional features to it (docstings and once-only evaluation, for example). Actually, I've built a whole dynamic web-site on it, and then faced a problem. As the code of the system grew quite complex with several layers of who-macros, SBCL started to lack resources to compile it. More specifically, upon &lt;b&gt;re-compilation&lt;/b&gt; of the system it sometimes fell into heap dump. We tried to investigate the problem, but couldn't dig deep enough to find it's cause (or just lacked enough knowledge to properly debug it).&lt;br /&gt;&lt;br /&gt;But the possibility of this solution indeed shows the nature of Lisp: virtually everything can be changed and all the boilerplate for that can be made transparent to the end-user. Alas it also shows, that this flexibility comes at a cost: it's pretty hard to build a robust and simple system, that will handle all the possible corner cases.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Transparent macroexpansion&lt;/b&gt; (idea and basic implementation by Victor Kryukov): the last approach (which I use now) was proposed in CL-WHO mailing list. It's in some sense the most obvious one: modify CL-WHO rules to accommodate macroexpansion.&lt;br /&gt;&lt;br /&gt;Here's the implementation of this idea, that I use, which is tightly based on the one, proposed by Victor:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(defparameter *who-macros* (make-array 0 :adjustable t :fill-pointer t)&lt;br /&gt;  "Vector of macros, that MACROEXPAND-TREE should expand.")&lt;br /&gt;&lt;br /&gt;(defun macroexpand-tree (tree)&lt;br /&gt;  "Recursively expand all macro from *WHO-MACROS* list in TREE."&lt;br /&gt;  (apply-to-tree&lt;br /&gt;   (lambda (subtree)&lt;br /&gt;     (macroexpand-tree (macroexpand-1 subtree)))&lt;br /&gt;   (lambda (subtree)&lt;br /&gt;     (and (consp subtree)&lt;br /&gt;   (find (first subtree) *who-macros*)))&lt;br /&gt;   tree))&lt;br /&gt;&lt;br /&gt;(defmacro def-internal-macro (name attrs &amp;body body)&lt;br /&gt;  "Define internal macro, that will be added to *WHO-MACROS*&lt;br /&gt;and macroexpanded during W-H-O processing.&lt;br /&gt;Other macros can be defined with DEF-INTERNAL-MACRO, but a better way&lt;br /&gt;would be to add additional wrapper, that will handle such issues, as&lt;br /&gt;multiple evaluation of macro arguments (frequently encountered) etc."&lt;br /&gt;  `(eval-when (:compile-toplevel :load-toplevel :execute)&lt;br /&gt;     (prog1 (defmacro ,name ,attrs&lt;br /&gt;              ,@body)&lt;br /&gt;       (unless (find ',name *who-macros*)&lt;br /&gt;         ;; the earlier the macro is defined, the faster it will be found&lt;br /&gt;         ;; (optimized for frequently used macros, like the inernal ones,&lt;br /&gt;         ;; defined first)&lt;br /&gt;         (vector-push-extend ',name *who-macros*)))))&lt;br /&gt;&lt;br /&gt;;; basic who-macros&lt;br /&gt;&lt;br /&gt;(def-internal-macro htm (&amp;rest rest)&lt;br /&gt;  "Defines macroexpasion for HTM special form."&lt;br /&gt;  (tree-to-commands rest '*who-stream*))&lt;br /&gt;&lt;br /&gt;(def-internal-macro str (form &amp;rest rest)&lt;br /&gt;  "Defines macroexpansion for STR special form."  &lt;br /&gt;  (declare (ignore rest))&lt;br /&gt;  (let ((result (gensym)))&lt;br /&gt;    `(let ((,result ,form))&lt;br /&gt;       (when ,result (princ ,result *who-stream*)))))&lt;br /&gt;&lt;br /&gt;(def-internal-macro esc (form &amp;rest rest)&lt;br /&gt;  "Defines macroexpansion for ESC special form."&lt;br /&gt;  (declare (ignore rest))&lt;br /&gt;  (let ((result (gensym)))&lt;br /&gt;    `(let ((,result ,form))&lt;br /&gt;       (when ,form (write-string (escape-string ,result)&lt;br /&gt;                                 *who-stream*)))))&lt;br /&gt;(def-internal-macro fmt (form &amp;rest rest)&lt;br /&gt;  "Defines macroexpansion for FMT special form."&lt;br /&gt;  `(format *who-stream* ,form ,@rest))&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;An interesting side-effect here is, that now HTM (et al.) becomes not just a symbol, but a normal macro, so this will work out of the box:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;CL-USER&gt; (with-html-output-to-string (s)&lt;br /&gt;           (:ul (dolist (a '(1 2 3))&lt;br /&gt;                  (test a))))&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And a user-level DEF-WHO-MACRO, that incorporates ONCE-ONLY (which is regularly needed for this domain) and docstring support can be defined like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(defmacro def-who-macro (name attrs &amp;body body)&lt;br /&gt;  "Define a macro, that will be expanded by W-H-O.&lt;br /&gt;/(Its name is added to *WHO-MACROS*).&lt;br /&gt;Body is expected to consist of an optional doctring and declaration,&lt;br /&gt;followed by a single backquoted WHO template.&lt;br /&gt;All regular, optional and keyword arguments are wrapped in ONCE-ONLY&lt;br /&gt;and can't contain pseudo-html forms."&lt;br /&gt;  `(eval-when (:compile-toplevel :load-toplevel :execute)&lt;br /&gt;     (unless (find ',name *who-macros*)&lt;br /&gt;       (vector-push-extend ',name *who-macros*))&lt;br /&gt;     (defmacro ,name ,attrs&lt;br /&gt;       ,@(when (cdr body) ; docstring&lt;br /&gt;           (butlast body))&lt;br /&gt;       ,(let ((attrs (if-it (or (position '&amp;rest attrs) (position '&amp;body attrs))&lt;br /&gt;                            (subseq attrs 0 it)&lt;br /&gt;                            attrs)))&lt;br /&gt;             (if attrs&lt;br /&gt;                 `(once-only ,(mapcar #`(car (as 'list _))&lt;br /&gt;                                      (remove-if #`(char= #\&amp; (elt (format nil "~a"&lt;br /&gt;                                                                           (car (mklist _)))&lt;br /&gt;                                                                   0))&lt;br /&gt;                                                 attrs))&lt;br /&gt;                    `(htm ,,(last1 body)))&lt;br /&gt;                 (last1 body))))))&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;I hope the above explanations and examples can be grasped easily. And moreover, that they will be useful for those, who are searching for the solution to the same problem. Still, if questions arise, be free to ask them.&lt;br /&gt;If someone uses a different approach, I'm very interested to hear about it.&lt;br /&gt;&lt;br /&gt;PS. Btw, Edi Weitz mentioned in the mailing list, that he is preparing a new version of the library. As he'd said, that addition of macroexpansion was on his TODO list, it will be interesting to see, what the new CL-WHO will hold...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-1206805284531530561?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/1206805284531530561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=1206805284531530561' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/1206805284531530561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/1206805284531530561'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/03/cl-who-macros.html' title='CL-WHO macros'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-5895484044099107150</id><published>2009-03-10T14:17:00.001+02:00</published><updated>2009-03-10T14:20:54.853+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>О самоподписных сертификатах</title><content type='html'>Написано для: &lt;a href="http://habrahabr.ru/blogs/infosecurity/54018/"&gt;habrahabr.ru&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;В связи с моим участием в проекте &lt;a href="http://fin-ack.com"&gt;fin-ack.com&lt;/a&gt; постоянно сталкиваюсь с подобными замечаниями: &lt;blockquote&gt;я не доверяю вашому самоподписному сертификату, почему вы не купите &amp;laquo;нормальный&amp;raquo; сертификат?&lt;/blockquote&gt;&lt;br /&gt;Как по мне, это один из случаев недопонимания и предрассудков, которых так много в отношении безопасности в Интернете. (Вроде знаменитых &lt;a href="http://lurkmore.ru/%D0%A5%D0%B0%D0%BA%D0%B5%D1%80%D1%8B,_%D0%BA%D1%80%D0%B5%D0%BA%D0%B5%D1%80%D1%8B,_%D1%81%D0%BF%D0%B0%D0%BC%D1%8B,_%D0%BA%D1%83%D0%BA%D0%B8"&gt;"Хакеров, крекеров, спамов, куки"&lt;/a&gt; :). Хочу разобрать его с двух точек зрения: как человека, некоторое время проработавшего в сфере защиты информации в банке и имевшего дело с большинством аспектов информационной безопасности, и как человека, занимающегося разработкой и развитием интернет-сервиса.&lt;br /&gt;&lt;br /&gt;Но сперва отвечу на вопрос, почему у нас нет "нормального" сертификата? (На самом деле, с недавнего времени есть :) Самая главная причина в том, что в нашем списке приоритетов этот пункт стоял на N-ном месте, и только сейчас все N-1 предыдущих пунктов были выполнены. Когда работаешь над новым проектом, всегда приходится от чего-то отказываться, потому что ресурсы, прежде всего временные, ограничены...&lt;br /&gt;&lt;br /&gt;&lt;em&gt;А почему же он стоял аж на N-ном месте?&lt;/em&gt;&lt;br /&gt;Во-первых, зачем вообще нужен сертификат SSL? Для того, чтобы зашифровать HTTP-соединение между браузером и сайтом, по которому будет передаваться пароль и какие-то другие конфиденциальные данные. Что изменится, если сертификат не подписан доверенным центром сертификации? Ничего! Соединение все равно будет зашифрованно точно также. Единственная возможная проблема: атака человек-посредине, которая в Интернете обычно является &lt;a href="http://en.wikipedia.org/wiki/Phishing"&gt;phishing'ом&lt;/a&gt; или &lt;a href="http://en.wikipedia.org/wiki/Pharming"&gt;pharming'ом&lt;/a&gt;.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;При фишинге пользователя перенаправляют на сайт с похожим URL. При этом в браузере обязательно появится предупреждение про сертификат (такое же предупреждение появляется и при первом заходе на реальный сайт с самоподписным сертификатом).&lt;br /&gt;&lt;img src="http://img.photobucket.com/albums/v473/pufpuf/Screenshot.png"&gt;&lt;br /&gt;В общем-то, в этой ситуации достаточно просто посмотреть к какому домену относится сертификат, и если это именно тот домен, на который вы хотели попасть, добавить сертификат в доверенные. После этого любое сообщение о недоверенном сертификате для данного сайта можно воспринимать как тревожный звоночек.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Отличие фарминга в том, что в данном случае пользователь попадет как-бы на тот сайт, на который хотел (судя по URL). Впрочем, ему также как и при фишинге будет показано сообщение о недоверенном сертификате.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Но многие вкладывают в сертификат SSL больший смысл:&lt;br /&gt;&lt;blockquote&gt;...Если же сертификат выдан каким-нибудь Verisign-ом (для примера), то это некая "гарантия" что за этим сайтом стоит настоящая организация/частное лицо и уж как минимум "есть с кого спросить в случае чего". Т.е. вообще это как гарантия "серьезности" намерений владельцев сайта.&lt;/blockquote&gt;&lt;br /&gt;Мы прекрасно понимаем, что такое мнение имеет право на жизнь. Но ведь все не так просто. Ничто не мешает купить сертификат у Verisign или другого вендора на липовую контору или подставные личные данные. Они не могут проверить наличие у клиента юридических оснований выдавать себя за условные ООО "Рога и копыта" из г.Пермь, Россйская Федерация. Единственное, что проверяется при выпуске сертификата &amp;mdash; это то, принадлежит ли вам домен, для которого вы его запрашиваете.&lt;br /&gt;Так что, как по мне, покупка сертфиката у Verisign'а &amp;mdash; это всего-навсего демонстрация того, что компания готова выбросить 500$ и несколько человеко-часов, а то и больше на утрясение всех организационных вопросов, вместо того, чтобы потратить это время и деньги на разработку новых возможностей или же реальное улучшение безопасности системы. Вообще, Verisign &amp;mdash; это для банков. Есть другие вендоры, с которыми проще и дешевле (пример &amp;mdash; ниже). &lt;br /&gt;&lt;br /&gt;&lt;em&gt;Но, самое главное, другое.&lt;/em&gt; Любая компьютерная система уязвима настолько, насколько уязвимо ее наименее защищенное звено. Хороший подход к безопасности &amp;mdash; это всегда комплекс мер, в котором нужно учесть все риски и уделить каждому должное внимание. Попробую перечислить основные риски безопасноти пользовательских данных для стандартного Интернет-проекта, имеющего дело с личной информацией (веб-почта, личная бухгалтерия и пр.) в порядке их важности:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Не достаточно продуманная система доступа к конфиденциальным данным, которая имеет дыры&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Проблемы в работе ПО, которое используется системой (ОС, веб-сервер, реализация протоколов шифрования), позволяющие осуществить взлом&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Атаки типа человек-посредине, социальная инженерия&lt;/li&gt;&lt;/ol&gt; &lt;br /&gt;Фишинг/фарминг (человек-посредине), по моему мнению, один из наименее важных рисков, поскольку его намного труднее осуществить, его быстро перекроют и, поэтому, такая атака имеет смысл только для систем с очень большим количеством пользователей, из которых можно быстро выудить очень ценные данные (классический пример: интернет-банкинг). По сравнению с этим намного проще запустить сканер уязвимостей и обнаружить, что в системе используется &lt;a href="http://jblevins.org/log/linux/ssh-vulnkey"&gt;старая версия OpenSSH&lt;/a&gt; или на Windows не установлена какая-то заплатка (к нам каждый день стучатся тысячи таких тестировщиков :). Или обнаружить какую-то &lt;a href="http://en.wikipedia.org/wiki/Cross-site_scripting"&gt;XSS&lt;/a&gt; или &lt;a href="http://en.wikipedia.org/wiki/SQL_injection"&gt;SQL-injection&lt;/a&gt; уязвимость. Это не говоря о более &lt;a href="http://seclists.org/fulldisclosure/2009/Mar/0029.html"&gt;сложных&lt;/a&gt; проблемах создания безопасных Интернет-систем, таких как, например, &lt;a href="http://en.wikipedia.org/wiki/HTTP_cookie#Drawbacks_of_cookies"&gt;корректное использование сессий (и куки)&lt;/a&gt; для аутентификации. Именно этому нужно уделять внимание в первую очередь!&lt;br /&gt;&lt;br /&gt;Еще один аспект безопасности, связанный с сертификатами. Будь он самоподписным или выданным Verisign'ом, все равно с ним ассоциирован секретный ключ, который нужно где-то хранить. Более того, он постоянно используется веб-сервером при открытии HTTPS-соединений, т.е. его нельзя применить один раз при включнии питания, сохранить на флешку и спрятать в сейф. Что будет, если кто-то завладеет ключом? (программист, который имел доступ к серверу, взломщик или еще кто-то). В идеале, этот ключ зашифрован, но при желании и наличии ресурсов его можно расшифровать (и сейчас это дешевле, чем организовать фишинг-атаку). А ведь мы не учли, что некоторые веб-сервера или реверс-прокси вообще не умеют работать с зашифрованными ключами. А еще ведь пароль может быть захардкожен где-то в тексте программы или скрипта, который ее запускает... Так что то, что на каком-то сайте красуется бирочка, что его SSL сертификат подписан Verisign, не дает никакой гарантии, что в один прекрасный день не появится фарминг-аналог, использующий тот же сертификат с украденным секретным ключом.&lt;br /&gt;&lt;br /&gt;Тут я даже не вспоминаю о таких аспектах, связанных с системой PKI, как особенности ее поддержки на разных специфических платформах, таких как j2me...&lt;br /&gt;&lt;br /&gt;Резюме: есть вещи, которые, в целом, правильные, но не всегда стоят затраченных усилий. Концентрация стартапов должна быть на другом, а мелочи, подобные "правильным" сертификатам должны идти вторым эшелоном. Сначала, как говорят американцы, нужно "get the product right". Всему свое время.&lt;br /&gt;&lt;br /&gt;P.S. Собственно говоря, я понимаю, что чем пытатся изменить общественное мнение, проще под него подстроится, поэтому у нас уже есть "правильный" сертификат (время подошло). Кстати говоря, который стоит в 10 раз дешевле, чем большинство (спасибо GoDaddy!). Цель данной статьи в первую очередь в том, чтобы еще раз коснуться неисчерпаемой темы информационной безопасности в Интернете и постараться правильно расставить акценты в одном из ее аспектов.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-5895484044099107150?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/5895484044099107150/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=5895484044099107150' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5895484044099107150'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5895484044099107150'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/03/blog-post.html' title='О самоподписных сертификатах'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-1653714276899903411</id><published>2009-02-26T11:41:00.006+02:00</published><updated>2009-02-28T11:48:03.315+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='etc'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Курс "Системное программирование и операционные системы"</title><content type='html'>Читаю сейчас этот курс в КПИ и хотел найти желающих (так сказать, гуру в этой области) для проведения пары гостевых лекций. Если поставить себя на место руководителя компании, находящегося в постоянном поиске сотрудников, или даже проджект менеджера, которому тоже нужно участвовать в подборе персонала для своего проекта, мне это было бы интересно даже с чисто утилитарной точки зрения. Не говоря уже об общественной полезности :)&lt;br /&gt;&lt;br /&gt;Попробовал обратиться через форум devua: http://www.developers.org.ua/forum/topic/384. Пока безрезультатно. Может, я не туда обращаюсь или у нас так просто не принято?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-1653714276899903411?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/1653714276899903411/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=1653714276899903411' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/1653714276899903411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/1653714276899903411'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/02/blog-post.html' title='Курс &quot;Системное программирование и операционные системы&quot;'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-4764601613655961302</id><published>2009-02-17T22:48:00.003+02:00</published><updated>2009-03-29T23:17:48.316+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>Sending SMTP mail with UTF-8 characters from Common Lisp</title><content type='html'>Today I explored the topic of sending a base64-encoded SMTP message and it turned out to be rather tricky. I discovered, that for this task (if you don't rely on Franz's infrastructure) effectively 4 libraries should be used. And as they are very scarcely documented, I decided to write this short description.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;CL-SMTP&lt;/h3&gt;&lt;br /&gt;Initially I knew very little about SMTP protocol (except for HELO and EHLO). So I started with the plain CL-SMTP functionality of SEND-EMAIL [1]. The function is not documented and has no own errors, it just re-signals the errors of the underlying USOCKET library. That's why it required some effort on my part to understand, why the following code produces USOCKET:UKNOWN-ERROR [2]:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;code&gt;(cl-smtp:send-email "localhost" "noreply@our.domain.net" "test@gmail.com"&lt;br /&gt;                    "subject"&lt;br /&gt;                    "тест")&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;em&gt;Explanation: Sending mail through the SMTP server on &lt;b&gt;localhost&lt;/b&gt; from &lt;b&gt;noreply@test.com&lt;/b&gt; to &lt;b&gt;test@gmail.com&lt;/b&gt; with the &lt;b&gt;body&lt;/b&gt; "тест".&lt;/em&gt;&lt;/blockquote&gt;&lt;br /&gt;It turned out, that the SMTP server just didn't accept the non-ascii characters in the body, because the default encoding is 7bit.&lt;br /&gt;In the process I discovered the useful debugging feature of CL-SMTP: &lt;code&gt;(setf cl-smtp::*debug* t)&lt;/code&gt; [3]. It will print the SMTP interaction log.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;CL-MIME&lt;/h3&gt;&lt;br /&gt;So I asked Google and found this &lt;a href="http://netzhansa.blogspot.com/2008/03/feature-development-and-getting-carried.html"&gt;article&lt;/a&gt; by Hans Hübner, where he explains his enhancements of CL-SMTP (currently integrated in the codebase) and describes how to send attachments with it. But to properly apply his examples to my case I first had to learn a couple of things about &lt;a href="en.wikipedia.org/wiki/MIME"&gt;MIME&lt;/a&gt;. In the example Hans uses &lt;b&gt;multipart/mixed&lt;/b&gt; Content-type for sending a message with attachments. But it is not necessary for the simple task of sending a text message in UTF-8 charset. For that you can use &lt;b&gt;text/plain&lt;/b&gt; Content-type and UTF-8 &lt;b&gt;charset&lt;/b&gt;. But for non-ascii symbols to be accepted by the mail server they should be encoded (usually) in base64 (&lt;b&gt;Content-encoding&lt;/b&gt; header). All this activities are handled with CL-MIME library. The library is quite self-explanatory so the lack of documentation doesn't hurt, except for a couple of moments.&lt;br /&gt;First of all, properly formatted MIME text data is produced with the function PRINT-MIME [4], which takes the CLOS MIME object with the appropriately set fields. The problem is, that the generated data contains both MIME headers and the part, which should go into the message body. So the function's output can't be used as an argument to SEND-EMAIL, because the headers will go to the data section, and the mail-client won't consider them (which will result in decoded body). For this case (and other cases, when you need more control of the process of SMTP interaction) Hans has created a high-level macro WITH-SMTP-MAIL [5]. There's a little catch in it as well: unlike SEND-EMAIL it accepts the list of recipients (while the former &amp;mdash; a sole recipient string).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;CL-BASE64 &amp; ARNESI&lt;/h3&gt;&lt;br /&gt;The second thing, which caused me most trouble, actually, was the tricky and once again undocumented handling of :CONTENT initarg of the MIME objects [6]. When you provide :ENCODING initarg, such as, primarily, :BASE64, the content part of the data, emitted with PRINT-MIME, will be subjected to the appropriate encoding (performed by CL-BASE64). The interesting thing is, that it will produce wrong output for UTF-8 strings. The proper argument format is an &lt;b&gt;octet array&lt;/b&gt;. And you need a function to reduce the string to this format.&lt;br /&gt;&lt;br /&gt;ARNESI is a useful library. It provides a lot of small utilities from different spheres. So I was glad to find out, that the needed function STRING-TO-OCTETS [7] is provided by it, because the lib was already utilized in my project.&lt;br /&gt;&lt;br /&gt;It's worth mentioning, that if non-ascii characters are used inside MIME body, they can be sent as is. But, AFAIU, it's not so robust as in base64 encoded form.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Result&lt;/h3&gt;&lt;br /&gt;So the final code turned out to be like this:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;code&gt;(defun send-email (text &amp;rest reciepients)&lt;br /&gt;  "Generic send SMTP mail with some TEXT to RECIEPIENTS"&lt;br /&gt;  (cl-smtp:with-smtp-mail (out "localhost" "noreply@fin-ack.com" reciepients)&lt;br /&gt;    (cl-mime:print-mime out&lt;br /&gt;                        (make-instance 'cl-mime:text-mime&lt;br /&gt;                                       :encoding :base64 :charset "UTF-8"&lt;br /&gt;                                       :content (arnesi:string-to-octets text :utf-8))&lt;br /&gt;                        t t)))&lt;/code&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Lessons learned&lt;/h3&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;To send plain text ascii email use CL-SMTP:SEND-EMAIL&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If USOCKET:UKNOWN-ERROR is signaled, most probably, the arguments are not properly formatted&lt;/li&gt;&lt;br /&gt;&lt;li&gt;For debugging (setf cl-smtp::*debug* t)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;To efficiently use MIME utilize CL-SMTP:WITH-SMTP-EMAIL in conjunction with CL-MIME:PRINT-MIME&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You need to supply an octet vector, not a string to CL-MIME:TEXT-MIME's :CONTENT initarg.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;To break a UTF-8 string into octets use ARNESI:STRING-TO-OCTETS&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-4764601613655961302?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/4764601613655961302/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=4764601613655961302' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/4764601613655961302'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/4764601613655961302'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/02/sendind-smtp-mail-with-utf-8-characters.html' title='Sending SMTP mail with UTF-8 characters from Common Lisp'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-6225709605200771045</id><published>2009-02-01T11:54:00.002+02:00</published><updated>2009-02-01T11:56:41.006+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='en'/><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><title type='text'>Nokia Locate Sensor</title><content type='html'>From idea:&lt;br /&gt;&lt;blockquote&gt;Received: by 10.141.28.2 with HTTP; Mon, 22 Oct 2007 05:25:31 -0700 (PDT)&lt;br /&gt;Message-ID: &lt;89dc7c5b0710220525p4e33dd50i6cb85d85cc1b0903@mail.gmail.com&gt;&lt;br /&gt;Date: Mon, 22 Oct 2007 15:25:31 +0300&lt;br /&gt;From: Vsevolod &lt;vseloved@gmail.com&gt;&lt;br /&gt;To: info@janchipchase.com&lt;br /&gt;Subject: idea to help not forget phones&lt;br /&gt;MIME-Version: 1.0&lt;br /&gt;Content-Type: multipart/alternative;&lt;br /&gt;boundary="----=_Part_8479_1626797.1193055931431"&lt;br /&gt;Delivered-To: vseloved@gmail.com&lt;br /&gt;&lt;br /&gt;------=_Part_8479_1626797.1193055931431&lt;br /&gt;Content-Type: text/plain; charset=ISO-8859-1&lt;br /&gt;Content-Transfer-Encoding: 7bit&lt;br /&gt;Content-Disposition: inline&lt;br /&gt;&lt;br /&gt;Hello Jan,&lt;br /&gt;&lt;br /&gt;My name is Vsevolod Dyomkin. I wanted to share a design idea related to&lt;br /&gt;mobile phones with you, as an only person I can presently reach, who can&lt;br /&gt;possibly facilitate its implementation.&lt;br /&gt;&lt;br /&gt;Today I have seen your presentation at TED.com concerning the uses of mobile&lt;br /&gt;phones. What interested me in it is the mention of places, which serve as&lt;br /&gt;gravity centers for important carried items. In my opinion, the existence of&lt;br /&gt;such places, in spite of the natural need for them, brings about one big&lt;br /&gt;potential inconvenience…&lt;br /&gt;&lt;br /&gt;Take my example, I work inside a big building and at my workplace the level&lt;br /&gt;of mobile signal is very poor, so I'm forced to leave my phone in the other&lt;br /&gt;part of the room where the connectivity is better. This sometimes leads to&lt;br /&gt;unpleasant situations when I forget the phone, leaving the room and&lt;br /&gt;building. Other example can be, when a person comes to a party and leaves&lt;br /&gt;her phone in some place not to be distracted by it. Afterwards he'll pretty&lt;br /&gt;probably forget about it. This may not only be the case with phones, but&lt;br /&gt;also with other carried items as well.&lt;br /&gt;&lt;br /&gt;To prevent such situations I've come up with the following idea: to have a&lt;br /&gt;small device which informs you (like beeps), when you part, for example, 10&lt;br /&gt;meters from the item. It will consist of two parts — a number of RFID tags&lt;br /&gt;(in a form-factor of small round colored stickers), which can be sticked to&lt;br /&gt;a mobile phone, a key, an id card etc. and a receiver/speaker, which can be&lt;br /&gt;a charm on a keyring or a bangle, which beeps. The receiver can optionally&lt;br /&gt;show the color of a sticker, which caused an alarm.&lt;br /&gt;&lt;br /&gt;To me this is an example of delegation of mundane/error-prone tasks to&lt;br /&gt;technology :-) — in this case the delegation of the necessity to flap one's&lt;br /&gt;pockets...&lt;br /&gt;&lt;br /&gt;If you find it interesting, fell free to contact me&lt;br /&gt;Best regards&lt;br /&gt;Vsevolod&lt;/blockquote&gt;&lt;br /&gt;...to &lt;a href="http://www.electricpig.co.uk/2009/01/09/nokia-locate-sensor-never-lose-anything-again/"&gt;implementation &lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-6225709605200771045?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/6225709605200771045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=6225709605200771045' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6225709605200771045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6225709605200771045'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/02/nokia-locate-sensor.html' title='Nokia Locate Sensor'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-3894073556971772523</id><published>2009-02-01T00:49:00.005+02:00</published><updated>2009-02-28T11:46:12.492+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><category scheme='http://www.blogger.com/atom/ns#' term='cl'/><title type='text'>How lexical scope is important</title><content type='html'>&lt;a href="http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/5e5023f416411973"&gt; "Fexprs more flexible, powerful, easier to learn? (Newlisp vs CL)" @ c.l.l.&lt;/a&gt;&lt;br /&gt;Rainer Joswig (with some participation from Kaz Kylheku and Pascal Bourguignon) on a practical example explain, what problems of dynamic scope (still used in the suggested "improved" newLisp, which turns out to be old, actually :) are solved by lexical scope.&lt;br /&gt;Bonus: how to create lisp-style special global variables in C++ (and a discussion of what can be improved in CL in this regard)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-3894073556971772523?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/3894073556971772523/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=3894073556971772523' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3894073556971772523'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3894073556971772523'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2009/02/how-lexical-scope-is-important.html' title='How lexical scope is important'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-2812857260244448719</id><published>2008-12-25T10:44:00.001+02:00</published><updated>2008-12-25T10:49:12.096+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Технология надежной и удобной аутентификация для web</title><content type='html'>&lt;span id="for-and-date"&gt;&lt;strong&gt;Написано для:&lt;/strong&gt; &lt;a href="http://vseloved.habrahabr.ru/blog/18305/"&gt;habrahabr.ru&lt;/a&gt;&lt;br /&gt;&lt;strong&gt;Время написания:&lt;/strong&gt; январь 2008&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;На данный момент все способы аутентификации, применяемые в веб-приложениях, либо недостаточно безопасны, либо слишком неудобны в использовании. Именно из-за этого все еще не появилась глобальная система микроплатежей через интернет.&lt;br /&gt;&lt;br /&gt;В чем именно неэффективность каждого из существующих способов?&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Простой пароль: удобно, но есть несколько угроз, и самая главная даже не столько несанкционированное ознакомление с ним, сколько то, что примерно одна и та же комбинация логин/пароль может быть использована для множества разнообразных сервисов, часть из которых может быть недостаточно защищена.&lt;br /&gt;&lt;li&gt;Одноразовые пароли: безопасно и относительно удобно (но, все-таки, добавляется лишнее устройство), но довольно дорого.&lt;br /&gt;&lt;li&gt;Сертификаты цифровой подписи: безопасно, но очень неудобно (проблемы с кросс-платформенной поддержкой токенов), а также дорого.&lt;br /&gt;&lt;li&gt;Использование второго канала связи для подтверждения (обычно, мобильного телефона): относительно безопасно, относительно удобно, относительно масштабируемо (пока...).&lt;br /&gt;&lt;li&gt;OpenID: безопасно, но на данный момент труднодоступно из-за того, что у 99% людей нет доверенного веб-сервера.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Однако, сейчас уже можно замахнуться на глобальную систему аутентификации, если использовать сочетание ставших уже реальностью 3 феноменов:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;IPv6;&lt;br /&gt;&lt;li&gt;OpenID;&lt;br /&gt;&lt;li&gt;стабильное интернет подключение с мобильного телефона/коммуникатора.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;strong&gt;Вот она:&lt;/strong&gt;&lt;br /&gt;Каждый мобильный телефон, находясь в сети провайдера, будет постоянно подключен к интернету и иметь статический IPv6 адрес, а также DNS вида &amp;lt;номер телефона&amp;gt;.&amp;lt;домен оператора&amp;gt;. В каждом телефоне будет встроен сервис OpenID.&lt;br /&gt;Таким образом, человеку нужно будет лишь каждое утро логиниться на свой телефон для того, чтобы иметь возможность автоматически аутентифицироваться на любом сайте. В такой системе, разумеется, появляется уязвимое место &amp;mdash; сам телефон, в случае завладения которым, злоумышленники могут выдать себя за его владельца. Но тут, даже на первый взгляд, видится достаточно много способов защиты:&lt;ol start='0'&gt;&lt;br /&gt;&lt;li&gt;(не говоря о блокировке телефона по звонку оператору);&lt;br /&gt;&lt;li&gt;для каких-то чувствительных тракзакций (например, платежей) можно сделать дополнительную авторизацию в виде, например, пароля (вот уже и двухфакторная аутентификация);&lt;br /&gt;&lt;li&gt;можно добавить биометрическую аутентификацию или использование дополнительного токена, например, RFID-брелка, который человек может носить на связке ключей, шее или запястье, и который должен находится не дальше, например, 2 м от телефона, чтобы работал OpenID сервис.&lt;br /&gt;Думаю, есть и другие разумные способы...&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;В такой системе мобильные операторы могут занять роль мобильных микро-банков, если платежи будут осуществляться прямо с личного счета у оператора, или же провайдеров аутентификационных услуг (уже сейчас появляются первые попытки реализовать этот подход, но с проприетарными системами аутентификации, которые не имеют перспектив масштабирования за рамки отдельных платежных систем).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-2812857260244448719?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/2812857260244448719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=2812857260244448719' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/2812857260244448719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/2812857260244448719'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/12/web.html' title='Технология надежной и удобной аутентификация для web'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-3930492257956879897</id><published>2008-12-23T10:01:00.003+02:00</published><updated>2009-02-28T11:46:44.338+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><category scheme='http://www.blogger.com/atom/ns#' term='cl'/><title type='text'>Surprise with Lambdas</title><content type='html'>Tobias Rittweiler posted an explanation about &lt;a href="http://trittweiler.blogspot.com/2008/12/on-relationship-of-lambda-and-lambda.html"&gt;the difference between (lambda and #'(lambda&lt;/a&gt;. The article is really useful, because I remember several times seeing the question in discussions unanswered.&lt;br /&gt;Well, after reading &lt;a href="http://letoverlambda.com/"&gt;LoL&lt;/a&gt; I sticked to using (lambda variant and I consider it to be the right thing: the less tokens, the less questions and confusion.&lt;br /&gt;&lt;br /&gt;All I can add is, that Common Lisp continues to surprise me with how well thought it's design is. So, throw away &lt;a href="http://lisp-univ-etc.blogspot.com/2008/12/1-namespace-to-rule-them-all.html"&gt;the reader macros&lt;/a&gt; &amp;mdash; it works by itself :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-3930492257956879897?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/3930492257956879897/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=3930492257956879897' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3930492257956879897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3930492257956879897'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/12/surprise-with-lambdas.html' title='Surprise with Lambdas'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-8978610495001015814</id><published>2008-12-19T10:07:00.002+02:00</published><updated>2008-12-19T10:21:47.705+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Мой текущий проект: fin-ack.com</title><content type='html'>&lt;span id="for-and-date"&gt;&lt;strong&gt;Написано для:&lt;/strong&gt; &lt;a href="http://habrahabr.ru/blogs/startup/47250"&gt;habrahabr.ru&lt;/a&gt;&lt;br /&gt;&lt;strong&gt;Время написания:&lt;/strong&gt; декабрь 2008&lt;/span&gt;&lt;br /&gt;&lt;h2&gt;1. Кому нужен учет личных финансов?&lt;/h2&gt;&lt;br /&gt;Многие люди считают, что они не ведут личной бухгалтерии, поскольку у них нет на это времени или им этот аспект жизни не интересен. Для кого-то это действительно так (но только в течение некоторого времени). Но, если задуматься, практически все городские жители вынуждены в той или иной форме вести учет своих финансов. Вопрос только в том, насколько он организован и какие средства для этого используются.&lt;br /&gt;&lt;br /&gt;Под учетом я понимаю не скурпулезное записывание каждой траты и дохода, а весь спектр вопросов контроля своего финансового положения, планирования бюджета, управления своими активами и пассивами (долгами, депозитами, кредитами и т.п.), анализа расходов и доходов. Кто-то просто держит в памяти все это &amp;mdash; и забывает в самый критический момент. Кто-то записывает на бумажке &amp;mdash; и потом тратит на ее поиски драгоценное время, когда вдруг оказывается необходимым разобраться в каком-то финансовом вопросе, имевшем место пару месяцев назад. Повторюсь, тот или иной учет ведет каждый, поскольку в современном мире на деньги завязано очень много отношений и событий (от работы и учебы до своего дома).&lt;br /&gt;&lt;br /&gt;Учет становится необходим, когда человек начинает пользоваться большим количеством финансовых продуктов. Даже несколько депозитов уже требуют для управления собой какого-то решения, а что говорить, когда добавляются еще кредиты, инвестиционные фонды и т.д. Его обязательно нужно вести фрилансерам, частным предпринимателям и любого рода бизнесменам. От учета может выиграть каждый, поскольку сможет лучше разобраться в своих расходах и доходах и составить какой-то план на будущее. Возможно, удастся увидеть те сферы, в которых можно экономить, задуматься о сбережениях и благотворительности. В любой книге "о том, как разбогатеть" написано, что это первый шаг к финансовой независимости. &lt;br /&gt;&lt;br /&gt;Разумеется, многие осознали для себя необходимость вести организованный учет (а кого-то это заставляют делать те или иные жизненные обстоятельства) и уже используют какие-то средства. Я, например, начинал с обычных текстовых файлов. Большинство, конечно, предпочтут Excel из-за встроенных в него финансовых функций. Кто-то идет дальше и использует для этого специализированные программы: GNU Cash, MS Money, Quicken или еще одну из сотни аналогов. Многие из этих программ написаны для PDA и дают огромное преимущество того, что находятся всегда под рукой, однако теряют в функциональности...&lt;br /&gt;&lt;br /&gt;Этот подход, безусловно, самый простой. Самый ли удобный? Все зависит от ситуации.&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Если человек постоянно перемещается или, во всяком случае, работает на нескольких компьютерах, у него возникает проблема синхронизации данных между рабочими местами.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Другой, пожалуй даже большей головной болью является забота о резервном копировании.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Безопасность, на первый взгляд, кажется в лучшем состоянии по сравнению с хранением своих данных у 3-х лиц, однако, PDA можно потерять или в него могут приспокойно заглянуть коллеги по работе или знакомые (пока вы куда-то отлучились), а в файлы на стационарном компьютере тоже может залезть кто-то. Самое неприятное тут то, что это может сделать кто-то из тех, кто вас знает, а, по сути, только такие люди могут быть заинтересованы в том, чтобы узнать о ваших финансах.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ну и, наконец, функциональность. Excel хорош своей гибкостью, но для того, чтобы создать в нем полноценную систему управления финансами нужно очень хорошо потрудиться. Да и, в целом, это все же инструмент для анализа, а не хранения данных, поэтому при росте их объема могут возникать непредвиденные трудности.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Я думаю, понятно, что мы предлагаем подход к учету финансов в облаке, на веб. Он лишен описанных выше недостатков, однако, безусловно, имеет определенные особенности. Разумеется, идея не оригинальна, в том числе и в сфере личных финансов. Когда я заинтересовался этим вопросом года 2 назад, уже начали появляться подобные системы. На западе их уже, наверное, около десятка. Зачем нужна еще одна?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;2. Наш подход и принципы, на которых построен сервис&lt;/h2&gt;&lt;br /&gt;Повторюсь, у каждого, наверно, есть примеры того, как веб-средства хранения личной информации выводят работу с ней на новый уровень. Забавно видеть, когда коллеги из разных компаний, которые пользуются корпоративной почтой, теряют время и бизнес-возможности из-за того, что оказываясь вне офиса (в коммандировке или на больничном) не могут получить доступ к ней. В итоге, все равно, приходится использовать G- или Y-mail. А сколько денег приходится тратить компаниям для обеспечения доступа к корпоративной почте через веб?.. Для меня такой killer app в свое время стал del.icio.us, который вернул мне возможность полноценно и легко пользоваться букмарками. Кстати, именно del.icio.us (в его предыдущей инкарнации) был вдохновением в дизайне пользовательского интерфейса для fin-ack.&lt;br /&gt;&lt;br /&gt;В сфере управления небольшими объемами данных веб-сервис &amp;mdash; это, как говорят, американцы win-win. И разработка с поддержкой проще и дешевле, и использование. Веб-сервис выигрывает за счет эффекта масштаба. Если каждому из тысячи пользователей нужно потратить на организацию бэкапа своих данных 10 минут, то даже если проработка сложной и надежной схемы резервного копирования займет у разработчиков день, эти 8 часов все равно не сравнить с 160 часами, потраченными каждым пользователем в отдельности. Также и на поддержку.&lt;br /&gt;&lt;br /&gt;Чем же уникально наше решение? Конечно, я изучал то, что уже есть, а также (по возможности) мнения людей. Говорят, главная проблема с учетом финансов в том, что людям лень постоянно вводить свои траты и доходы. На этом предположении даже основана система 4konverta. В Америке эту проблему решают по своему: поскольку у них 90% рассчетов происходит по карточкам (т.е. в безналичной форме), они берут информацию о тратах прямо у банков, и каждая из систем пытается конкурировать в том, кто лучше автоматически обработает эту информацию, категоризирует ее и т.д. (По-моему, это выброс усилий на ветер). Да и удивляешься доверчивости американцев, спокойно отдающим аутентификационные данные от своего счета третьей стороне. Скольку уже было скандалов с кражей личных данных у любых компаний, начиная с AOL?! Другим недостатком такого подхода является то, что их системы заточены под автоматический учет трат, поэтому у них зачастую даже нет возможности вносить их вручную (например, насколько я помню, так устроена система Mint).&lt;br /&gt;&lt;br /&gt;Как человек, который вводит свои расходы/доходы позволю себе не согласиться с тем, что людям лень. &lt;b&gt;Им, просто, не удобно&lt;/b&gt;. И у нас на это есть несколько ответов.&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Во-первых, нужно самому придумать удобный для себя workflow. Например, мне совершенно не интересно, что в моем чеке из супермаркета 20 наименований продуктов. Я запишу их просто как "еда" и поставлю общую сумму. В среднем за день мне нужно ввести в систему 3-5 записей, а занимает это 3-5 минут. За это время я могу посмотреть на свои балансы, подумать о планах, сделать еще какой-то анализ.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Интерфейс системы ввода должен быть удобным (это, вроде как, понятно, но, почему-то, не всегда делается)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Не нужно запоминать свои траты &amp;mdash; их нужно вводить сразу. Для этого мы разработали специальный &lt;b&gt;мобильный клиент&lt;/b&gt;. У которого к тому же есть некоторые другие функции, актуальные для устройства в вашем кармане.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Следующий важнейший вопрос &amp;mdash; это безопасность. Очень многие не хотят хранить свои финансовые данные у третьих сторон. В этом есть как рациональное, так и эмоциональное зерно. Как по мне, личная переписка ценнее финансовых данных (если, конечно, вы не занимаетесь противозаконными операциями), а она уже давно перекочевала на веб. У нас опять же несколько ответов на этот вопрос:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Мы сохраняем полную анонимность пользователей. При регистрации не требуется вводить никаких личных данных (даже электронной почты). Единственный способ идентифицировать пользователя, который остается &amp;mdash; IP-адрес &amp;mdash; мы обязуемся не хранить в соответствии с Пользовательским соглашением. (Естественно, если использовать прокси, то и такая возможность идентификации отпадает). Единственное, мы собираемся использовать информацию о местоположении с точностью до города, однако это никак не нарушает анонимность.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Передача данных защищена SSL. Данные хранятся на нашем сервере, а не у третьих сторон.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ну и, в конце концов, опять же, все зависит от пользователя. Если есть опасения на предмет чистоты той или иной транзакции, или не стоит вносить ее в систему, или можно внести в какой-то непонятной для других форме.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Что немаловажно, конфиденциальность пользовательских данных, защищает &lt;b&gt;бизнес-модель&lt;/b&gt; нашей компании. В Интернете очень много бесплатных сервисов, но в сфере обработки личной информации бесплатность часто является плохим признаком. Почему? Понятно, что сервис создается не из альтруистических побуждений, а так или иначе для заработка денег. Как? Кто-то может надеятся на авось: "вот наберем миллион пользователей и к нам будет стоять очередь покупателей". Это значит, что велика вероятность того, что сервис долго не проживет, и придется искать другой. Кто-то рассчитывает на рекламную модель, но в этой сфере она не будет работать (слишком мало времени пользователю интересно проводить на таких сайтах, не говоря уже об использовании мобильных клиентов, которые вообще сводят это время практически на нет). Точнее, она может заработать тогда, когда разработчики будут готовы делиться данными своих пользователей с третьими сторонами. Короче говоря, бесплатный сыр &amp;mdash; только в мышеловке, и кому, как не людям, которые учитывают свои финансы, не понимать, что за все нужно платить &amp;mdash; вопрос только в том, стоит ли оно того?&lt;br /&gt;&lt;br /&gt;Мы выбрали хорошо зарекомендовавшую себя в этой сфере (например, проектами 37signals) модель частично платной системы. Это значит, что те функции, которые являются действительно отличительными, будут иметь небольшую абонплату. В то же время базовой учетной системой всегда можно будет пользоваться бесплатно. Более того, счета будут выставляться &lt;b&gt;по истечении 3-х месяцев постфактум&lt;/b&gt;, т.е. будет возможность полноценно попробовать систему в течение этого срока, а затем решить, использовать ли ее дальше и в какой форме: платной или бесплатной. При этом мы никому не будем передавать или продавать данные пользователей!&lt;br /&gt;&lt;br /&gt;Важным вопросом мы считаем удобство в оплате услуг системы, а также сохранение анонимности при этом. Я думаю, тема работы с платежными системами заслуживает отдельной статьи, которую я напишу в ближайшее время, когда будут урегулированны все организационные моменты и можно будет подвести итоги.&lt;br /&gt;&lt;br /&gt;Вообще, один из главных принципов нашей системы &amp;mdash; гибкость. Мы считаем, что пользователю самому виднее, как удобнее управлять своими финансамии, как работать с нами, а наша задача в том, чтобы дать ему:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;инструменты для решения своих задач;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;рассказать, как ими можно пользоваться;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;показать примеры разных подходов к их применению.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Fin-ack.com не является Web2.0 приложением, но он к этому движется. По моему мнению, в большинстве случаев создания веб-сервисов именно так и нужно поступать: сначала должна быть добротная базовая система, которая не хуже оффлайн аналогов. А по мере привлечения пользователей она должна развиваться вместе с ними и создавать возможности, просто недоступные для своих оффлайн конкурентов. Это и есть реализация одного из принципов Web2.0 по Тиму О'Рейлли: системы, которая становится тем лучше, чем больше людей ее используют. У нас есть идеи, как можно использовать аггрегированную информацию множества пользователей в их интересах, но об этом не стоит говорить до их появления. Я уверен, что многие идеи прийдут от пользователей.&lt;br /&gt;&lt;br /&gt;В то же время мы движемся и дальше &amp;mdash; к Web3.0 :) Если так можно назвать тенденцию, которую тот же Тим О'Рейлли называет "transcend the web", а другие &amp;mdash; тем, что большинство людей в будущем будут работать с веб-приложениями посредством своего мобильного телефона. А в мобильный нельзя "запихнуть" веб-сайт. Приложения для него должны разрабатываться с учетом ограничений этого устройства, а также тех контекстных возможномтей которые оно дает.&lt;br /&gt;&lt;br /&gt;Рассказывать о конкретных особенностях нашей системы не буду, статья и так уже достаточно большая. Надеюсь, &lt;a href="http://fin-ack.com"&gt;fin-ack.com&lt;/a&gt; сам способен рассказать о себе.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;PS.&lt;/h2&gt;&lt;br /&gt;Почему в теме Lisp? Ну, потому что вся серверная часть написана на Lisp... :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-8978610495001015814?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/8978610495001015814/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=8978610495001015814' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8978610495001015814'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8978610495001015814'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/12/fin-ackcom.html' title='Мой текущий проект: fin-ack.com'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-8517594668902494402</id><published>2008-12-15T18:37:00.005+02:00</published><updated>2008-12-16T10:25:55.760+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Парадигмы программирования</title><content type='html'>Со всем спектром придуманных на сегодня парадигм программирования можно ознакомиться в &lt;a href="http://en.wikipedia.org/wiki/Programming_paradigm"&gt;Википедии&lt;/a&gt;. В этой статье я хотел остановиться на тех из них, которые имеют значительное влияние на современные языки и программные среды, и о которых, соответственно, обязательно иметь представление любому разработчику. Обязательно потому, что за каждой из парадигм стоит огромная работа по поиску путей решения типичных проблем, возникающих при программировании "интуитивным"[1] путем, и не учитывать этот опыт &amp;mdash; значит наступать на те же грабли в который раз и заново придумывать велосипед. Разумеется, многое из написанного в статье общеизвестно, но несмотря на это дискуссии на эти (базовые) темы возникают вновь и вновь. Эта статья &amp;mdash; попытка упорядочить для себя общую картину, а также установить точку отсчета, к которой можно было бы привязываться впоследствии.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Основные парадигмы&lt;/h2&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Структурное программирование&lt;/b&gt;&lt;br /&gt;Эта парадигма представляет в основном теоретическое значение (хотя и является неотъемлемой частью практически всех современных языков). По сути, это был первый &lt;b&gt;сдвиг парадигмы&lt;/b&gt; в программировании, когда после появления первых абстрактных и высокоуровневых по сравнению с ассемблером языков, пришло осознание, что разрабатывать на них также нужно на более высоком уровне абстракции, чем на ассемблере: в терминах условных выражений и циклов, а не передачи управления между метками. И, что не удивительно, был достигнут консенсус, неоднократно с тех пор подтвержденный практикой, что при использовании более абстрактных конструкций программирование становится более доступным, а программы &amp;mdash; расширяемыми, поддерживаемыми,&amp;mdash; и, в целом, можно решать намного более сложные задачи. &lt;br /&gt;Эта парадигма основывается на работах Дейкстры, Хоара, Вирта и других, которые доказали, что любое вычисление можно выразить через 3 базовые операции:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;последовательное выполнение;&lt;br /&gt;&lt;li&gt;условный переход;&lt;br /&gt;&lt;li&gt;цикл с условием.&lt;/ul&gt;&lt;br /&gt;Девиз структурного программирования &amp;mdash; "GOTO является вредным".&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Объектно-ориентированное программирование&lt;/b&gt;&lt;br /&gt;Это самая распространенная на сегодняшний день парадигма, которая является развитием идей структурного программирования. Она подается как реализация "естественного" взгляда на окружающий мир, в котором всё является объектом. Она, как известно, покоится на 3-х китах:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;инкапсуляция;&lt;br /&gt;&lt;li&gt;наследование;&lt;br /&gt;&lt;li&gt;полиморфизм.&lt;/ul&gt;&lt;br /&gt;Однако, учитывая распространение, которое она приобрела, а также, наверное, тот факт, что понятие "естественного" и строго математического определения немного отличаются, каждый ОО-язык понимает эти 3 концепции по-своему (во всяком случае, последние две), и каждое такое понимание имеет право на жизнь и применение.&lt;br /&gt;Наследование включает, условно говоря, наследование "свойств" и "функциональности".&lt;br /&gt;Для свойств оно может быть основано на классе (от абстрактного к конкретному) &amp;mdash; см. C++, Java,&amp;mdash; или же на прототипе (от конкретного к абстрактному) &amp;mdash; JavaScript.&lt;br /&gt;Наследование же функциональности и полиморфизм &amp;mdash; это 2 стороны одной медали. В Lisp подходе, основанном на родовых функциях эти 2 концепции унифицируются в рамках одной абстракции. Наследование функциональности может пониматься по-разному: как наследование реализации или как наследование интерфейса (см. http://weblog.raganwald.com/2008/04/is-strictly-equivalent-to.html). С другой стороны спектра находится обобщение подхода родовых функций &amp;mdash; мультиметоды Clojure &amp;mdash; диспетчиризация не только по типу аргументов, а и по любому предикату от аргументов.&lt;br /&gt;&lt;br /&gt;Самой распространненой, однако не единственной рализацией ОО-модели является придуманная в SmallTalk и перенятая в той или иной степени C++, Java, Python и другими основными современнымя языками модель передачи сообщений (диспетчиризация метода по типу первого аргумента, который является объектом, принимающим сообщение). Этот подход я бы еще назвал субъектно-ориентированным программированием, потому что в нем неявно считается, что каждый объект совершает действия, т.е. становится субъектом. В зависимости от динамичности языка в нем может поддерживаться утиная типизация (Duck typing), согласно которой для вызова метода для объекта этот объект должен иметь метод с такой сигнатурой, при этом его тип может не проверяться (динамическая передача сообщений).&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Функциональное программирование&lt;/b&gt;&lt;br /&gt;Эта парадигма имеет свои корни в Лямбда-исчислении Черча и ее первой реализации &amp;mdash; оригинальному Lisp'у &amp;mdash; больше лет, чем первому структурному языку Algol-60.&lt;br /&gt;Сейчас функциональная парадигма (в форме декларативного программирования) противопоставляется императивному подходу. Ее основа &amp;mdash; это функция в математическом понимании (преобразование входных данных), а не функция как процедура, меняющая состяние мира.&lt;br /&gt;Концепциями функциональной парадигмы являются:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;программирование без побочных эффектов (ссылочная прозрачность)&lt;br /&gt;&lt;li&gt;применение функций высших порядков (итерации с помощью map и рекурсии, карринг, композиция...)&lt;/ul&gt;&lt;br /&gt;Основным направлением в ФП сейчас направлением являются т.н. "чисто" функциональные языки, которые реализуют такие концепции, как:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;ленивые вычисления&lt;br /&gt;&lt;li&gt;строгая типизация и вывод типов&lt;br /&gt;&lt;li&gt;сопоставление с образцом (pattern matching)&lt;/ul&gt;&lt;br /&gt;Это Haskell, ML (Ocaml, SML), Scala и Qi.&lt;br /&gt;Однако, и динамические (не поддерживающие строгую типизацию) функциональные языки (как правило, имеющие Lisp-основу) также существуют и активно развиваются: Scheme, Erlang, Clojure.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Мета-программирование&lt;/b&gt;&lt;br /&gt;Основными идеями этой парадигмы являются возможности расширения базового языка превоклассными (такими, которые смогут использоваться наравне со встроенными) конструкциями, а также всегда программирование на уровне адекватном логике решаемой задачи и прдеметной области. Мета-программирование поддерживается методологией проектирования и разработки снизу-вверх, при которой сложная система разбивается на уровни, каждый из которых соответствует определенным независимым горизонтельным задачам: от уровня утилит-расширений языка вплоть до уровня доменно-специфического языка для моделирования той или иной прикладной области,&amp;mdash; и реализуется постепенно уровень за уровнем. При этом на каждом уровне сложность задачи не увеличивается, поскольку примитивами на нем являются абстракции, выработанные на более низких уровнях.&lt;br /&gt;Таким образом мета-рограммирование порождает концепцию DSL &amp;mdash; доменно специфических языков, создаваемых для той или иной предметной области (языко-ориентированное программирование), одновременно и  использующими возможности хост-языка (все его базовые подсистемы, такие как: сбор мусора, математические библиотеки и т.д.), и являющимися адаптированными в синтаксисе и семантике к той сфере, для которой они реализованы.&lt;br /&gt;К мета-программным возможностям можно отнести те или иные особенности многих языков. Например, в C это перпроцессор, в C++ &amp;mdash; в каком-то смысле, перегрузка операторов, а также применение шаблонов для абстракции на уровне системы типов. В Python &amp;mdash; это механизм декораторов, позволяющий динамически дополнять реализацию методов. Попытки добавить определенные макросистемы делаются во многих языках. Это, например, MetaLua, а также модули andand, rewrite, rubymacros и др. в Ruby и т.д. Безусловно, базовым языком для парадигмы мета-программирования является Lisp, в котором и зародились (и продолжают зарождатся) ее концепции.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Скриптинговые языки&lt;/b&gt;&lt;br /&gt;Можно сказать, что это недопарадигма, поскольку она не основывается на какие-то фундаментальные исследования или принципы. Основная идея тут &amp;mdash; максимальное удобство и простота ad-hoc реализации опеределенного класса решений на определенной архитектуре (иными словами, практичность). Это обуславливает такие характеристики языка, как:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;динамичность&lt;br /&gt;&lt;li&gt;интерпретируемость&lt;br /&gt;&lt;li&gt;привязка к хост-системе&lt;/ul&gt;&lt;br /&gt;В рамках этой парадигмы получили свое рождение такие языки, как: Perl (и PHP), Python, Ruby, Tcl, Lua, Basic, JavaScipt, Shell-языки (Bash, PowerShell, ActionScript, AppleScript, ...), некоторые из которых впоследствии переросли ее и перешли, как правило, в категорию объектно-ориентированных языков.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Программирование, ориентированное на параллелизм&lt;/b&gt;&lt;br /&gt;Это самая новая парадигма и, пожалуй, еще не до конца сформировавшаяся, поскольку пока что нет косенсуса на счет того, какая из предложенных концепций станет общепризнанной (если это вообще произойдет). А это и развитие архитектуры, подобной MapReduce Google, и асинхронная передача сообщений между процессами (полностью без общего состояния) Erlang'а, и использование программной транзакционной памяти &amp;mdash; обобщения концепции транзакции в базах данных на любое вычисление (Haskell, Clojure). Основой для выделение этого подхода в отдельную парадигму стало понимание того, что использование блоков для синхронизации параллельных вычислений не является масштабируемым и поддерживаемым решением при реализации многопоточности (в том числе см. http://jcp.org/en/jsr/detail?id=166). Можно провести параллели между этой парадигмой и процедурной: проблема с использованием блоков аналогична проблеме goto. Пока что ясно одно: эта парадигма станет развитием функционального подхода с его краеугольными камнями немутируемых структур данных и ссылочной целостности.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;Дуализм типизации&lt;/h2&gt;&lt;br /&gt;Несмотря на распространенное мнение поддержка языком программирования того или иного вида типизации является ортогональным к парадигме, которую этот язык реализует. Более того, вопрос типизации можно рассматривать в 2-х аспектах:&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Слабая vs сильная&lt;/b&gt;. Это различие скорее количественное, чем качественное. "Абсолютно" сильная типизация (как это реализовано в Haskell'е) не позволяет никакого приведения типов (во время исполнения). Более слабые системы типизации дают такую возможность до определенной степени. Например, многие источники называют С слабо-типизированным языком, поскольку он позволяет выполнять неявное приведение типов, а также явное приведение указателей (что не дает возможность проверить тип на этапе компиляции).&lt;br /&gt;&lt;li&gt;&lt;b&gt;Статическая (проверка типов во время компиляции) vs динамическая (проверка на этапе исполнения)&lt;/b&gt;. Суть динамической типизации можно изложить во фразе из Common Lisp: "у переменных нет типов, типы есть только у значений". Это не значит, что типы не проверяются (как это происходит в нетипизированных языках, тких как Assembler и Forth, но они проверяются при выполнении операций над конкретными значениями во время исполнения программы. Поэтому в динамическом с сильной типизацией Lisp'е не может возникнуть ошибки сегментации памяти при попытке использовать значение не того типа, которая часто встречается в статическом со слабой типизацией С. По мнению идеологов статической типизации, благодаря ее использованию можно исправить большой класс ошибок в программе, связанных с использованием несоответствующих типов, с помощью явного указания типов и их проверки компилятором. А также увеличить быстродействие программы благодаря отсутствию необходимости проверки типов во время исполнения. В то же время написание таких программ является более длительным и сложным процессом (языки со строгой статической типизацией еще называют bondage &amp; discipline languages), и что самое неприятное, их становится очень трудно менять впоследствии. Поэтому, наверное, ни тот ни другой подход не являются предпочтительными в общем случае и могут быть оба использованы в зависимости от конкретной задачи и преференций разработчиков. В идеале, должны появиться языки, которые будут позволять задействовать обе системы параллельно или на выбор. Например, в Common Lisp есть возможность объявлять типы для выражений и переменных для того, чтобы отключить их проверку во время исполнения &amp;mdash; это решает проблему скорости для динамического языка, однако не адресует вопрос статической проверки типов при компиляции.&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;h2&gt;Ссылки на другие интересные парадигмы&lt;/h2&gt;&lt;br /&gt;Нельзя сказать, что языки, реализующие эти парадигмы широко используются, однако они играют значительную роль в некоторых важных узких областях и показывают интересные альтернативные концепции, которым можно найти применение при решении задач в малоисследованных направлениях.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Программирование на основе стека&lt;/b&gt;&lt;br /&gt;Языки на основе стека &amp;mdash; это очень простые языки, примитивные операции которых построенны вокруг манипуляции этой структурой данных. Такой подход, в основном, избавляет от необходимости использования переменных для ссылок на значения (point-free programming). Кроме того, синтаксис таких языков становится весьма унифицированным (как правило, используется постфиксная нотация), что дает возможность задействовать мета-программирование.&lt;br /&gt;Языками на основе стека являются PostScript и Forth. Кроме того, большинство виртуальных машин современных языков, такие как JVM, .Net CLR, Perl Parrot и т.д. также являются языками основанными на стеке.&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Логическое программирование&lt;/b&gt;&lt;br /&gt;Это реализация в языке модели формальной логики. При этом результаты работы программы часто являются побочными эффектами логического вывода. Интерес таких языков в том, что они предлагают совершенно иную модель вычислений, чем фон Неймановская архитектура или Лямбда-исчесление Черча, соответственно некоторые задачи, например, связанные с праллельными вычислениями или имеющие четкую математическую модель, можно решать с применением совсем других подходов. В рамках этой парадигмы получили развитие такие концепции, как бэктрекинг и сравнение с образцом (pattern matching).&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Программирование в массивах&lt;/b&gt;&lt;br /&gt;Это направление было введено в APL и продолжает развиваться в его последователях J/K/Q, также его реализации присутствуют в MatLab'е и Mathematica. Базовая идея заключается в том, чтобы обобщить скалярные операции на векторные типы данных. Кроме того в этих языках, как правило поддерживается point-free programming. Их удобно использовать для выполнения сложных математических вычислений и они представляют скорее исследовательский интерес. Интересным примером промышленной системы с использованием таких языков является БД kdb+. &lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;P.S&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;В заключение хотелось бы упомянуть еще одну парадигму или, скорее, анти-парадигму: эзотерические языки (эзоязыки). Эту концепцию также называют Turing tarpit (бочка дегтя Тюринга), потому что эзоязыки показывают, что можно легко создать Тюринг-полный язык, на котором будет &lt;em&gt;совершенно невозможно программировать&lt;/em&gt;. По-моему, это хороший урок для тех, кто утверждает, что язык программирования не имеет значения. Да, на любом Тюринг-полном языке можно реализовать любой алгоритм и любую программу, но поробуйте сделать это на brainfuck'е, использующем только 8 допустимых символов и 8 операций, на котором "Hello world" выглядит так: &lt;pre&gt;&lt;code&gt;++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-]&gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++.&gt;.+++.------.--------.&gt;+.&gt;.&lt;/code&gt;&lt;/pre&gt;,  или на Whitespace, для которого весь набор значащих символов составляют пробел, табуляция и новая строка.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;small&gt;[1] Как верно замечено в книге "&lt;a href="http://www.pianofundamentals.com"&gt;Основы практики на пианино&lt;/a&gt;" в сложных областях деятельности существует настолько много методов решения задач, что даже не смотря на неплохие способности человеческого мозга навскидку выделять более эффектиные из них (которые мы называем "интуитивными"), даже среди этого подмножества есть группа методов, которые являются на порядки эффективнее других, но для того, чтобы их найти, простой интуиции не достаточно.&lt;/small&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-8517594668902494402?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/8517594668902494402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=8517594668902494402' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8517594668902494402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8517594668902494402'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/12/blog-post.html' title='Парадигмы программирования'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-932138303155637129</id><published>2008-12-14T23:42:00.003+02:00</published><updated>2009-02-28T11:47:06.302+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><category scheme='http://www.blogger.com/atom/ns#' term='cl'/><title type='text'>1 namespace to rule them all</title><content type='html'>This thing scares and confuses me: &lt;code&gt;filter even [1..10]&lt;/code&gt;&lt;br /&gt;Is even a function or a var?&lt;br /&gt;Personally I'd prefer: &lt;code&gt;filter #'even [1..10]&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Common Lisp's n-namespace property is one of the most underestimated features of this most underestimated language. In CL you virtually always know, what you are dealing with, a variable or a function. "Phew!" you say, "in C++ as well". Not so fast. If you try to use callbacks you don't. And when we enter the realm of the functional paradigm, where functions fly all around like daggers  in the infamous house, it's really good to know for sure.&lt;br /&gt;&lt;br /&gt;Let's restate the rules:&lt;br /&gt;1. Prefix notation: in first position of a form should always be a name of a function/macro/special operator (unless it's an inner form of a macro, which introduces its own structure). So:&lt;br /&gt;* (+ 1 2), not (1 + 2)&lt;br /&gt;* (print 1) -- just like print(1)&lt;br /&gt;* (with-open-file (in "test.txt") ...) -- a structural macro&lt;br /&gt;&lt;br /&gt;2. If you want to pass a function somewhere #' is your best friend. Thus you can (fun)call it, apply it, map/filter with it etc.&lt;br /&gt;&lt;br /&gt;3. Finally we need to add one more ingredient to the recipe -- the ability to use (lambda ...) forms in function position. We can do it with such a reader macro:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;(set-dispatch-macro-character #\# #\f&lt;br /&gt;     (lambda (stream subchar arg)&lt;br /&gt;       (declare (ignore subchar)&lt;br /&gt;  (ignore arg))&lt;br /&gt;       (let ((sexp (read stream t nil t)))&lt;br /&gt;         (let ((fname (gensym)))&lt;br /&gt;           (setf (symbol-function fname) (eval `(lambda ,@sexp)))&lt;br /&gt;           fname))))&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;which allows us to write this code:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;CL-USER&gt; (#f((a b) (+ a b)) 1 2)&lt;br /&gt;3&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-932138303155637129?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/932138303155637129/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=932138303155637129' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/932138303155637129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/932138303155637129'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/12/1-namespace-to-rule-them-all.html' title='1 namespace to rule them all'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-2128463354654660746</id><published>2008-10-26T20:15:00.003+02:00</published><updated>2008-10-26T22:59:30.703+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Парадокс Монти Холла</title><content type='html'>Пример того, как программистский подход к решению проблемы может сделать даже &lt;a href="http://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D1%80%D0%B0%D0%B4%D0%BE%D0%BA%D1%81_%D0%9C%D0%BE%D0%BD%D1%82%D0%B8_%D0%A5%D0%BE%D0%BB%D0%BB%D0%B0"&gt;парадокс&lt;/a&gt; тривиально понятным.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(defun monty-hall (n)&lt;br /&gt;  "Возвращает отношение доли успешных и неуспешных розыгрышей&lt;br /&gt;игры Монти Холла при выборе стратегии всегда менять изначально&lt;br /&gt;выбранную дверь"&lt;br /&gt;  (let ((succ 0.0)&lt;br /&gt;        (fail 0.0))&lt;br /&gt;    (dotimes (i n)&lt;br /&gt;      (let ((choice (random 3)) &lt;br /&gt;            (prize (random 3)))&lt;br /&gt;        (if (= choice prize)&lt;br /&gt;            (incf fail) ; мы выбрали правильную дверь, но придется поменять&lt;br /&gt;            (incf succ)))) ; мы выбрали неправильную дверь, значит, после смены -- правильную&lt;br /&gt;    (/ succ fail)))&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;И статистика:&lt;br /&gt;CL-USER&gt; (monty-hall 100)&lt;br /&gt;1.5641025&lt;br /&gt;CL-USER&gt; (monty-hall 1000)&lt;br /&gt;1.6455027&lt;br /&gt;CL-USER&gt; (monty-hall 10000)&lt;br /&gt;1.9958059&lt;br /&gt;CL-USER&gt; (monty-hall 100000)&lt;br /&gt;2.0147724&lt;br /&gt;CL-USER&gt; (monty-hall 1000000)&lt;br /&gt;1.9966408&lt;br /&gt;CL-USER&gt; (monty-hall 10000000)&lt;br /&gt;1.9986455&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-2128463354654660746?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/2128463354654660746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=2128463354654660746' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/2128463354654660746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/2128463354654660746'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/10/blog-post.html' title='Парадокс Монти Холла'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-8341906506741420080</id><published>2008-10-26T20:06:00.006+02:00</published><updated>2009-02-28T11:47:20.865+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><category scheme='http://www.blogger.com/atom/ns#' term='cl'/><title type='text'>Monty Hall Paradox</title><content type='html'>The example of how tackling a problem in a "hacker's hat" can make a &lt;a href="http://en.wikipedia.org/wiki/Monty_Hall_problem"&gt;paradox&lt;/a&gt; trivial to understand.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;(defun monty-hall (n)&lt;br /&gt;  "Returns a ratio of success rate to failure rate, if we follow&lt;br /&gt;the strategy of always switching the initially selected door in&lt;br /&gt;the Monty Hall Problem"&lt;br /&gt;  (let ((succ 0.0)&lt;br /&gt;        (fail 0.0))&lt;br /&gt;    (dotimes (i n)&lt;br /&gt;      (let ((choice (random 3)) &lt;br /&gt;            (prize (random 3)))&lt;br /&gt;        (if (= choice prize)&lt;br /&gt;            (incf fail) ; we've chosen the right door, but will need to switch&lt;br /&gt;            (incf succ)))) ; we've chosen the wrong door and after switching -- the right&lt;br /&gt;    (/ succ fail)))&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And the statistics:&lt;br /&gt;CL-USER&gt; (monty-hall 100)&lt;br /&gt;1.5641025&lt;br /&gt;CL-USER&gt; (monty-hall 1000)&lt;br /&gt;1.6455027&lt;br /&gt;CL-USER&gt; (monty-hall 10000)&lt;br /&gt;1.9958059&lt;br /&gt;CL-USER&gt; (monty-hall 100000)&lt;br /&gt;2.0147724&lt;br /&gt;CL-USER&gt; (monty-hall 1000000)&lt;br /&gt;1.9966408&lt;br /&gt;CL-USER&gt; (monty-hall 10000000)&lt;br /&gt;1.9986455&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-8341906506741420080?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/8341906506741420080/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=8341906506741420080' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8341906506741420080'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8341906506741420080'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/10/monty-hall-paradox.html' title='Monty Hall Paradox'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-8575807349386759788</id><published>2008-10-23T09:02:00.005+03:00</published><updated>2008-10-23T09:44:43.944+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>"Clojure for Lisp Programmers" Talk Summary</title><content type='html'>In his &lt;a href="http://clojure.blip.tv/file/1313398"&gt;talk&lt;/a&gt; Rich Hickey made an overview of the major features of Clojure and the design rationale behind them. Except for being Lisp-1, as far as I understand, Clojure is mostly based in and inspired by Common Lisp. Moreover, it tries to push some of the CL technologies to even purer abstraction and generality.&lt;br /&gt;&lt;br /&gt;I've tried to categorize the discussed features in their relation to Common Lisp:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Taken from CL and stretched/modified&lt;br /&gt;&lt;ul&gt;&lt;li&gt;destructuring&lt;br /&gt;&lt;li&gt;nil behavior in conditionals&lt;br /&gt;&lt;li&gt;defmacro&lt;br /&gt;&lt;li&gt;multimethods with dispatch by an arbitrary function of arguments&lt;br /&gt;&lt;blockquote&gt;Obviously is more general, than the CL variant, although Rich failed to mention, that CL's generic functions have also dispatch by EQL, which is enough for, I think, 95% of the situations.&lt;/blockquote&gt;&lt;br /&gt;&lt;li&gt;first-class immutable &amp; persistent data-types: list, vector, hash-map and set&lt;br /&gt;&lt;li&gt;seq(uence) protocol&lt;br /&gt;&lt;blockquote&gt;I consider the last 2 a very important idea and a good candidate for implementation as a CL library.&lt;/blockquote&gt;&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;Intentionally different from CL&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Side-effect free reader &amp; separate symbol resolution for macros&lt;br /&gt;&lt;blockquote&gt;This is also a well thought-out choice, which gives a lot of food for thought.&lt;/blockquote&gt;&lt;br /&gt;&lt;li&gt;Lisp-1. Symbols and vars separated (in Lisp-2 vars are symbol-value or symbol-function)&lt;br /&gt;&lt;blockquote&gt;In my opinion, not the best option. But it's, obviously, the simpler one to implement&lt;/blockquote&gt;&lt;br /&gt;&lt;li&gt;egal equality&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;Additional technologies&lt;br /&gt;&lt;ul&gt;&lt;li&gt;STM (MVCC), agents &amp; refs, commute&lt;br /&gt;&lt;blockquote&gt;A full-fledged innovative concurrent infrastructure.&lt;/blockquote&gt;&lt;br /&gt;&lt;li&gt;Call-ability of data-structures as vars&lt;br /&gt;&lt;blockquote&gt;A controversial choice, which may lead to a lot of confusion.&lt;/blockquote&gt;&lt;br /&gt;&lt;li&gt;Metadata&lt;br /&gt;&lt;blockquote&gt;In some sense, a substitute for lack of multiple-values. Besides, a partial introduction of support for the &lt;a href="http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html"&gt;prototype pattern&lt;/a&gt; (although lacking inheritance).&lt;/blockquote&gt;&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;Different from CL because of the need for JVM integration (CL solution being superior)&lt;br /&gt;&lt;ul&gt;&lt;li&gt;no multiple-values&lt;br /&gt;&lt;li&gt;namespaces instead of packages&lt;br /&gt;&lt;li&gt;boolean true/false&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;Arbitrary choices inferior to CL solutions (because too much work needed to implement &amp; some infrastructure already available)&lt;br /&gt;&lt;ul&gt;&lt;li&gt;exception handling&lt;br /&gt;&lt;li&gt;debugging &amp; profiling tools&lt;br /&gt;&lt;li&gt;boxed integers instead of numeric tower&lt;/ul&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-8575807349386759788?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/8575807349386759788/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=8575807349386759788' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8575807349386759788'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8575807349386759788'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/10/clojure-for-lisp-programmers-talk.html' title='&quot;Clojure for Lisp Programmers&quot; Talk Summary'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-5398940916668126413</id><published>2008-08-25T22:01:00.005+03:00</published><updated>2008-08-26T07:17:47.374+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>Re: An Acceptable Lisp</title><content type='html'>Recently I've been thinking about programming languages in general and how they relate to Common Lisp, spurred by such questions (and discussions) as &lt;a href="http://www.randomhacks.net/articles/2005/12/03/why-ruby-is-an-acceptable-lisp"&gt;Why Ruby is an acceptable Lisp?&lt;/a&gt;, &lt;a href="http://www.lispforum.com/viewtopic.php?f=2&amp;t=60"&gt;Does Common Lisp need a better type system?&lt;/a&gt;, &lt;a href="http://groups.google.com/group/comp.lang.lisp/browse_frm/thread/9948645b27a2d49e#"&gt;What do you LISPers think of Haskell?&lt;/a&gt;, &lt;a href="http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/6bbce408fd6a10fa"&gt;Paul Graham's Arc is released today... what is the long term impact?&lt;/a&gt; etc.&lt;br /&gt;&lt;br /&gt;As far as I understand, what people mean, when they talk about acceptable Lisp, is not Lisp per se, but a universal high-level programming language. It's just, that Lisp has made a bold claim to be such one. Is it? More on that later.&lt;br /&gt;&lt;br /&gt;...So, first I've analyzed the languages, which are wide-spread now, whether they can deliver universality, so to say.&lt;br /&gt;&lt;br /&gt;Kent Pitman has very accurately &lt;a href="http://developers.slashdot.org/article.pl?sid=01/11/13/0420226&amp;mode=nocomment"&gt;noted&lt;/a&gt; (Q16: Scheme in CS), that most people program in assembly languages, be they concrete, like C/C++, or abstract, like Java. And that is understandable, because these languages allow a straightforward solution, and for the majority is content with that. And that's one of the most important arguments, why purely functional programming languages can't become universally-accepted.&lt;br /&gt;&lt;br /&gt;What about high-level languages? First of all, what kind of an animal is that? My idea is, that it should be a language, in which you can declare what to do and not how, think of any problem in terms of its domain vocabulary and not in terms of moving bits and bytes around. But that's, obviously, an oversimplification. One of the most important qualities of a high-level language is, that it provides efficient means of abstraction (and the function is the most important &amp;mdash; that's why languages without first-class functions don't count as high-level). The high-level language should not only provide means to solve a real-world problem, but as well to solve a problem of solving such problems. Well, although I can't express concisely and clearly, what's that, I'm sure, that there exists a common understanding, or rather feeling, of that concept among the people, who raise such questions.&lt;br /&gt;&lt;br /&gt;So, what languages do we consider high-level?&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Python&lt;/strong&gt; is an attempt at a high-level language. But it's stuck in the middle of a road from imperative to high-level. The goal of the language's design was not to achieve universality, but to create a robust dynamically-typed object-oriented language for scripting, and it's reached, but there's no more ambitious aims ahead...&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Ruby&lt;/strong&gt; is definitely not an "acceptable Lisp" &amp;mdash; it's a &lt;a href="http://steve.yegge.googlepages.com/ruby-tour"&gt;clean &lt;strong&gt;Perl&lt;/strong&gt;&lt;/a&gt; (with a "mixin" of Smalltalk). The aim of a language is power, but in a somewhat myopic (Perl) view, i.e.: the ability to hack (which in the context is the opposite of build complex systems) neatly and fast. That's why everything, which was considered in the historical context of the language's creation as a powerful feature to have, was incorporated into it. From classes to keyword parameters to regular-expressions to anonymous functions. Surely a lot of the achievements of language design from Lisp ans Smalltalk and other languages are in Ruby, but it's not &lt;a href="http://weblog.raganwald.com/2008/02/turtles-all-way-down-please.html"&gt;"turtles all the way down"&lt;/a&gt; -- not uniform enough to be able to efficiently develop new high-level concepts on top of it, which will fit (so you're mostly left with what's already in the language. (Btw, Mr. Braitwaite tries to &lt;a href="http://weblog.raganwald.com/2008/06/not-going-dark.html"&gt;prove&lt;/a&gt; the opposite).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;C#&lt;/strong&gt; (it seems, they are incorporating first-class functions in C#3) is an interesting example of a language, which is based on C++ and Java and gradually moves to high-levelness. But still it lacks underlying vision of universality, it's very similar to Python in a way, that the imperative paradigm with it's class-oriented extension if considered the most important and basic one, thus limiting the others to being second-level citizens.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Erlang&lt;/strong&gt; is just a DSL for concurrency programming. It's functional, but not at all intended to be universal.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Haskell&lt;/strong&gt; is definitely different from the previously discussed languages, because it really is built on the basis of an abstract ideology and not as a pile of features. It's 2 fundamentals are pure functionality and type-inference, and as a manifestation of this ideas it achieves prominent results. But this ideas do not lead to a language, capable of adapting to every problem and producing the most natural solution.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Forth, J et al.&lt;/strong&gt;. Why they can't be universal? I think it's a debate between an ordinary to ourselves (alphabetic) language and an hieroglyphic one. They are definitely a way of their own, and a very interesting one, but not a way to unification, I think.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Scheme&lt;/strong&gt; is a high-level language, built from a set of Lisp "axioms", whose biggest flaw is prohibition of real macros. Paul Graham's &lt;strong&gt;Arc&lt;/strong&gt; is an attempt to bring macros to Scheme, but, i think, it's early to say, whether it will succeed as a separate language or a successor to Scheme.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Common Lisp&lt;/strong&gt;&lt;br /&gt;Is Common Lisp an acceptable Lisp, i.e. is it a universal high-level programming language? I don't think, that it is, and that has been proven by history, so to say (it's not even near to being universally accepted). But it, surely, is the most advanced language in this direction and it possesses some of the high-level features, that are not present in other languages (and these features are actually the features of "ideal" Lisp, from which Common Lisp derives its name). Briefly about them:&lt;br /&gt;* Parens are not a drawback. On the contrary, they are a powerful basis for abstraction, because they help to denote a form &amp;mdash; the universal building block of it. Form is both data and code. Then there are S-expressions which are supported by parens, but have the other underlying concept &amp;mdash; prefix notation, which unifies operations (mostly coded in infix notation in other languages) and function application (either prefix or suffix, but never infix). &lt;br /&gt;* As Paul Graham has &lt;a href="http://www.paulgraham.com/arcchallenge.html"&gt;pointed&lt;/a&gt;, the best way to design a language is to start from the most general set of axioms. And Lisp (Common Lisp included) follows this principle, moreover its set of axioms is probably the most general one.&lt;br /&gt;* Macros are built on top of Lisp's general-purpose s-expressions being code and data. I think it's obvious, why they are obligatory to a high-level programming language -- because syntactic abstraction is one of the most common ones. &lt;br /&gt;* No "religious" adherence to any specific programming paradigm, but taking the best principles from every one (at least at the time of language's design).&lt;br /&gt;* A concise standard. It's funny, when other language programmers "complain" about the CL standard being bloated, incomplete and outdated. Maybe, the only language, which can claim to have at least a somewhat accurate, complete and up to date one is Java (or rather could), while others either don't have any (Ruby, Python etc.) or have a lot of revisions, vendor-specific parts and incompatibilities between them (JavaScript, C++ and so on). And the fact, that the functional programming languages, which usually have clear formal specifications, are constantly in the process of revising them, as for me, seems to prove, that they just didn't reach maturity yet. &lt;br /&gt;&lt;br /&gt;But, still, there are some features, or rather underlying principles, that Common Lisp lacks.&lt;br /&gt;* What static typing folks want?&lt;br /&gt;I "belong" to the dynamic typing camp, but I think, I understand, what the other camp needs. The Lisp idea, that variables don't have types and only values have, should be acceptable to them. But they want a compiler subsystem, which checks one of the dimension of the program's correctness (the dimension, that really allows verification), based on the programmer's declaration. And that surely can be considered an important high-level feature. Moreover, it seems to be a reachable goal.&lt;br /&gt;What CL has is optional type declaration and runtime check. &lt;a href="http://www.lambdassociates.org/lC21.htm"&gt;Qi&lt;/a&gt; implements static type-checking and type-inference, and besides other functional-paradigm features. But it abandons prefix-notation and multi-paradigm approach as a whole. Thus it's a very advanced functional language with roots in Lisp, but not Lisp. Neither it is universal.&lt;br /&gt;What can be done &lt;em&gt;inside&lt;/em&gt; Lisp to add static type-checking to dynamic one? I think there's a possibility at least to implement partial "lexical" type-checking for lexical variables. But how to do that properly is a different question. Anyway, I'm sure, that although a "universal language's" type system can have a deep mathematical foundation, it should allow not only logical formalisms for defining types, but as well plainer and simpler variants.&lt;br /&gt;&lt;br /&gt;* What Schemers wanted, and why they chose Scheme?&lt;br /&gt;As far as I understand, people who choose Scheme over CL do that not because it nil and false, nor for the lack of macros. And not because it's Lisp-1, but to be more precise, because it supports evaluation of the car of a list, while CL only allows function (and macros, and special-forms) names in it. I don't see, why there can't be a compromise between the 2 approaches: leave multiple namespaces, which is, as practice shows, very beneficial, and at the same time treat the function cell (car of a list) differently: if it's a name, it should be from the function namespace, while if it's a list -- it's a form, which should be evaluated and produce either a function object or a function name.&lt;br /&gt;&lt;br /&gt;* Some idiosyncrasies, mostly inherited, should be removed (not all functions are generic; errors do not subclass standard-object etc.). The MOP should be in the standard. And the tradition of incorporating the best from every paradigm should be continued: at least the concurrency-oriented paradigm is relevant. &lt;br /&gt;&lt;br /&gt;* Community process.&lt;br /&gt;If the ideal Lisp is ever to appear it should have a standard and a process of modifying it &lt;em&gt;without loosing backward compatibility&lt;/em&gt;.&lt;br /&gt;I understand, what kind of a difficult and bureaucratic process was the effort for the Common Lisp standard, and that it's not going to be repeated. Well, it's not necessary. The times have changed, and now &lt;a href="http://www.sbcl.org"&gt;excellent implementations&lt;/a&gt; of a language are produced by the community, which is globally distributed. There's no central power and a big client (like DARPA), which demands &lt;em&gt;official&lt;/em&gt; standard. The standard should and could be produced by the community as a result of a consensus. Why Python, Perl or Arc will never become universal? Because their development is mostly determined by the tastes and preferences of their "benevolent dictators", and their views, whatever enlightened and advanced be them, are subjective and can't be accepted universally. Today there's virtually no other power in the Lisp world, except its users. Nor big organizations, like Microsoft, Google or DARPA, neither dominant implementation producers. So I think, that if the members of the CL community with the biggest impact on it, like the participants of the original standardization process, who are still active in the Common Lisp world: Kent Pitman and Daniel Weinreib,-- as well as the new generation of prominent lispers: Pascal Constanza, Edi Weitz, Paul Graham,-- and others were able to self-organize and setup a community process for the creation of the new Lisp standard, it could yield a desired result.&lt;br /&gt;&lt;br /&gt;Maybe it's time to start advancing from Common Lisp to Universal Lisp?..&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-5398940916668126413?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/5398940916668126413/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=5398940916668126413' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5398940916668126413'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/5398940916668126413'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/08/re-acceptable-lisp.html' title='Re: An Acceptable Lisp'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-6606548398235516749</id><published>2008-08-23T15:00:00.004+03:00</published><updated>2008-08-23T21:23:58.832+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Shell — секретное оружие Unix :)</title><content type='html'>&lt;span id="for-and-date"&gt;&lt;strong&gt;Написано для:&lt;/strong&gt; &lt;a href="http://vseloved.habrahabr.ru/blog/19885/"&gt;habrahabr.ru&lt;/a&gt;&lt;br /&gt;&lt;strong&gt;Время написания:&lt;/strong&gt; февраль 2008&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Как я написал в одном комментарии:&lt;br /&gt;&lt;blockquote&gt;если не хотите знать про коммандную строку, то в линуксе вам делать нечего. Будет вам та же винда, только вместо одних проблем получите другие...&lt;br /&gt;--comment605950&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;С другой стороны, как раз в shell заключается одно из кардинальных отличий Unix-систем, то, что делает эти системы открытыми...&lt;br /&gt;&lt;br /&gt;В чем же основные особенности Unix shell, которых нет в Windows?&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Весь процесс конфигурации и администрирования Unix построен на работе с текстовыми файлами. Shell — это полноценный язык программирования, заточенный для манипуляции строками и работы с тектсовыми файлами. К тому же, именно как расширение возможностей shell по обработке строк и возник Великий язык PERL!.&lt;br /&gt;&lt;li&gt;Благодаря п.1 shell — это отличный инструмент для постепенного изучения самой операционной системы.&lt;br /&gt;&lt;li&gt;Наличие shell способствует реализации важного принципа создания любых программ:&lt;br /&gt;&lt;blockquote&gt;Если это простая программа, которая предназначена для выполнения ограниченного числа операций, ее нужно реализовывать как набор команд, родных для платформы, на которой она работает (как правило, платформой является ОС). Если же имеем дело со сложной интерактивной программой с потенциально неограниченным набором операций — ее нужно реализовывать как язык опять же на родном для платформы носителе (сейчас наиболее родным и удобным носителем при взаимодействии с компьютерами все же является текст, а не звук, графика или что-либо еще).&lt;br /&gt;Примерами 1-го и 2-го подхода могу служить tcpdump, реализованный полностью в текстовом режиме, над котором не представляет труда надстроить интерфейс (по сути, Ethereal — и есть подобный интерфейс), и emacs — самый расширяемый текстовый редактор.&lt;br /&gt;В этом проявляется основное отличие философии Unix и Windows: в Win интерфейс програм по умолчанию делается на графическом языке, из-за чего программы практически невозможно сопрягать или строить на их основе новые. Потому что для этого нет поддержки на уровне ОС. И для того, чтобы сделать программы расширяемыми, все равно приходится использовать текст (пример — тот же VBA в Word и Excel).&lt;/blockquote&gt;&lt;br /&gt;&lt;li&gt;Автодополнение команд и, что самое главное, путей. Трудно даже представить, сколько времени и сил экономит эта небольшая возможность!&lt;br /&gt;&lt;li&gt;Полный набор утилит для всех возможных задач администрирования.&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Стоит также перечислить утилиты shell, которые обязательно нужно использовать в повседневной работе с системой:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;man — это справка по ОС, которая включает не только описание работы всех команд, но также и что должно быть в большинстве из конфигурационных файлов, а также много другой полезной информации;&lt;br /&gt;&lt;li&gt;less — просмотр текстовых файлов;&lt;br /&gt;&lt;li&gt;grep (программистов, не знающих о grep, не берут на работу в Amazon.com :);&lt;br /&gt;&lt;li&gt;locate — быстрый поиск файлов.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;а еще можно вспомнить группы утилит, использование которых намного удобнее и эффективнее их графических аналогов:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;настройки сети (позаимствованные Windows);&lt;br /&gt;&lt;li&gt;управления пакетами;&lt;br /&gt;&lt;li&gt;управления пользователями;&lt;br /&gt;&lt;li&gt;монтирования томов...&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Можно только повториться, что для тех, кто не хочет работать в shell, Linux или другая Unix-based ОС — во многом такая же неудобная и непонятная система, как и Windows. Но стоит все-таки попробовать разобраться с shell — хотя бы для того, чтобы посмотреть, как правильно администрируются информационные системы.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-6606548398235516749?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/6606548398235516749/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=6606548398235516749' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6606548398235516749'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6606548398235516749'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/08/shell-unix.html' title='Shell — секретное оружие Unix :)'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-8256630332797579789</id><published>2008-07-25T15:46:00.001+03:00</published><updated>2008-07-25T15:46:55.255+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>О пользе унификации и вреде предубеждений</title><content type='html'>&lt;span id="for-and-date"&gt;&lt;strong&gt;Написано для:&lt;/strong&gt; &lt;a href="http://www.developers.org.ua/archives/vseloved/2007/07/24/o-polze-unifikatsii-i-vrede-predubezhdeniy/"&gt;developers.org.ua&lt;/a&gt; (изменен код макро gcase)&lt;br /&gt;&lt;strong&gt;Время написания:&lt;/strong&gt; июль 2007&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;table&gt;&lt;tr&gt;&lt;td width=40%&gt;&lt;/td&gt;&lt;td width=60% style="{font-size:smaller;}"&gt;In fact, let’s not even worry about Java. Let’s not complain about Microsoft. Let’s not worry about them because we know how to program computers, too, and in fact we know how to do it in a meta-way. We can set up an alternative point of view, and we’re not the only ones who do this, as you’re well aware.[&lt;a href="#1"&gt;1&lt;/a&gt;]&lt;br /&gt;&amp;ndash;Alan Kay&lt;br /&gt;There is a huge difference working with a language where you have to wait a year to get a new loop statement from the language designer and compiler implementer - or - where you do it yourself in twenty minutes.[&lt;a href="#2"&gt;2&lt;/a&gt;]&lt;br /&gt;&amp;ndash;Rainer Joswig&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;1. Принципы хорошего стиля программирования&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;На Западе широко известна следующая концепция из классического учебника MIT по программированию “Структура и интерпретация компьютерных программ”:&lt;br /&gt;&lt;blockquote&gt;Сперва мы хотим утвердить идею, что компьютерный язык — это не просто способ добиться от компьютера выполнения операций, но, скорее, что это новое средство для формального выражения идей о методологии. Таким образом, программы должны быть написаны для людей, чтобы они их читали, и только между прочим для машин, чтобы они их исполняли.[&lt;a href="#3"&gt;3&lt;/a&gt;]&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Есть и другой подход: программа — как набор кубиков лего, которые нужно любой ценой состыковать вместе, а где совсем не сходиться — допилить напильником.&lt;br /&gt;&lt;br /&gt;Хороший стиль программирования должен обладать такими качествами как элегантность и максимально возможная простота понимания результатов. Это, в конце концов, есть мера трудозатрат на написание программы, ее отладку и поддержку, а также удовлетворения программиста от процесса труда.&lt;br /&gt;&lt;br /&gt;Кроме того, хороший стиль подразумевает, что все должно быть сказанно только 1 раз — не должно быть повторяющихся конструкций, реализующих один и тот же шаблон.&lt;br /&gt;&lt;br /&gt;К сожалению, современные мейнстрим языки жертвуют целью сделать возможным хороший стиль из коньюнктурных соображений реализации. В результате нам приходится использовать корявые языки, что приводит к полной неэффективности на более высоких уровнях абстракции. Мало того, что программист для того, чтобы получить возможность создания серьезных программ, вынужден вкладывать большое количество времени в изучение эзотерических технологий, которые через пару лет устареют (они устаревают как раз из-за своей ограниченности и привязки к конкретным промежуточным архитектурам), таких как: фабрики классов COM или же MFC на пару с Win32 API, AJAX и пр. Применение этих технологий серьезно затрудняет развитие достигнутых результатов другими людьми, которые не имеют времени, желания или возможности изучить их, не говоря уже о внутренних ограничениях самих технологий.&lt;br /&gt;&lt;br /&gt;Также и хорошая парадигма, как и любая другая идеология, дает большой выигрыш на начальном этапе, но становится тормозом развития впоследствии, и языки программирования созданные для реализации концепций одной парадигмы становятся ее заложниками. Человек лучше всего думает на естественном языке, и хороший язык программирования должен давать ему возможность наиболее естественно (т.е. близко к их оригинальной форме) выразить свои мысли о структурах данных и алгоритмах, которые составляют по Вирту все, из чего состоят программы, накладывая при этом как можно меньше ограничений. В языке программирования должны быть равные возможности реализации любой прадигмы, любой концепции, способной зародиться в голове программиста, наиболее простым и элегантным путем. Такой подход лежит в основе Lisp’а, “программируемого языка програмиирования”.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;2. Ограниченность языков с жестким синтаксисом&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Если говорить по существу, то первая проблема большинства языков — жесткий и часто весьма запутанный и не унифицированный синтаксис, который, если немного перефразировать слоган Perl’а, делает некоторые вещи (которые по мнению создателя языка должны быть простыми) простыми, а остальные — как прийдется. А приходится так, что, большинство концепций, которые не были приняты во внимание при создании языка, требуют огромных трудозатрат для реализацию теми, кто в них нуждается. В качестве примера можно привести такую вездесущую потребность, как передача функций в качестве параметров другим функциям (так называемым, higher-order functions), которую не возможно элегантно реализовать в императивных языках (С или Java). Можно обратиться к функциональным языкам, но и здесь мы видим, что в основу языка заложено множество семантически-нагруженных конструкций, об универсальности которых говорить не приходится.&lt;br /&gt;&lt;br /&gt;Важнейшей характеристикой любого языка программирования являются предоставляемые им средства абстракции. В идеале должна быть возможность абстрагировать типичные шаблоны и конструкции программного кода таким образом, чтобы не только в каждой программе использовать термины, хорошо соответствующие предметной области (абстракции процедур, функций и классов, доступные в большинстве языках), но также чтобы снять необходимость рутинного повторения более общих конструкций “среднего уровня”, которые нельзя отнести ни к самому языку, ни к конкретной предметной области. Только так можно обеспечить соблюдение принципа хорошего стиля — писать все только один раз.&lt;br /&gt;&lt;br /&gt;Показательным в этом отношении является шаблон with-..., который используется в Lisp’е в таких задачах, как работа с файлами и потоками, интерфейсами и протоколами обмена информацией. Примером может быть запись данных в файл. Это требует выполнения таких операций, как объявления переменной потока вывода, открытие физического файла на запись, при этом проверки на предмет того, возможно ли это, и выполнения соответсвующих операций в случае ошибок. Также не нужно забывать о необходимости закрыть файл после окончания манипуляций с данными. Все эти операции просты и рутинны, а необходимость их повторного написания много раз приводит к бесполезной трате времени, а также к тому, что файл в конце концов забывают закрыть… В Lisp эта констукция реализуется с помощью макро with-open-file в теле которого выполняются манипуляции с данными. Почему такой подход не применяется в других языках — об этом ниже.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;3. Синтаксис Lisp’а&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Почему-то распространено мнение, что у Lisp’а сложный синтаксис, впрочем обычно подразумевается другое — зачем все эти скобки??! А вот Lisp-программисты считают, что у Lisp’а вообще нет синтаксиса.&lt;br /&gt;&lt;br /&gt;На самом деле, как верно подмечено в комментарии Тима Бредшоу у Lisp’а синтаксис минимальных обязательств. Можно сказать, что он выполняет только лишь функцию необходимой регуляризации английского языка для того, чтобы исключить неоднозначность.&lt;br /&gt;&lt;br /&gt;Программы Lisp’а — это текст: есть атомы — символьные идентификаторы — это слова, есть список — атомы, разделенные пробелами и взятые в скобки — это предложение. Lisp Reader4 (можно легко представить себя на его месте) читает этот текст и интерпретирует его в качестве так называемых s-выражений, т.е. подходит к нему с таким набором предпосылок:&lt;br /&gt;    &lt;ul&gt;&lt;li&gt;в любом s-выражении первым идет имя операции, а остальные элементы могут быть s-выражениями либо атомами&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;любой атом является либо именем либо означает сам себя&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;таким образом, s-выражение имеет следующий вид: (имя элемент элемент ...), где элемент — список либо атом.&lt;br /&gt;&lt;br /&gt;Так зачем все-таки все эти скобки??! Риску начать издалека. Когда люди впервые занялись символьной математикой, они начали с использования простых операций — = + - * &gt; &lt; — которые имели 2 аргумента, т.е. выражения записывались в виде c = а + b. Им сразу же пришлось столкнуться с проблемой порядка операций (что будет, если записать d = a + b * c, и появились скобки, которые вернули в математику однозначность :). Чуть позднее появилась концепция функции, т.е. обощенной операции, которая имеет аргументы и значения. Поскольку многие функции также могут иметь несколько аргументов и даже несколько значений, их пришлось записывать, например, так: f(x, y, z) := (x, y) + (y, z). Здесь уже теряется единообразие: для базовых операций используется инфиксная нотация (т.е. операция записывается между своими аргументами), а для самой функции — по сути дела префиксная (или польская нотация). Что если попытаться свести все к единой нотации: := f x y z + (x y (y z. Не все понятно и однозначно. Убрать неоднозначность можно, добавив скобок: (define f (x y z) (+ (x y) (y z))). Выглядит не совсем привычно, но и изначальная конструкция тоже не была первым, что мы изучили в школе на математике.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;4. Метапрограммирование&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Что дает нам префиксная запись операций по сравнению, например, с С-подобным синтаксисом? Думаю, кому-то интересно будет узнать, что во внутренних структурах компилятора программа С выглядит так же. Я, конечно, не имею в виду, что там у них тоже скобки :). Но после прохождения сложного синтаксического разбора С-программа преобразуется компилятором в абстрактное синтаксическое дерево (AST). Код Lisp’а — это и есть AST, подавляющая часть которого скомпрессирована в виде MACRO’в (или, как находчиво придумал Conrad Barski — &lt;a href="http://lisperati.com/casting.html"&gt;SPEL’ов&lt;/a&gt;), записанный прямо у нас перед глазами. И, поскольку мы избавлены от необходимости трансляции кода программы из ее обычного синтаксиса в AST и обратно, открывается возможность для широкого использования MACRO’в: если мы видим повторяющиеся части в дереве, их легко абстрагировать.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://img.photobucket.com/albums/v473/pufpuf/macro.png" alt="упрощенный вариант преобразования AST, выполняемое с помощью MACRO with-open-file"&gt;&lt;br /&gt;&lt;br /&gt;На это можно посмотреть и с другой стороны:&lt;br /&gt;&lt;blockquote&gt;Lisp — это исполняемый XML с более дружественным синтаксисом.[&lt;a href="#4"&gt;4&lt;/a&gt;]&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Ведь XML-файл — это тоже по сути дерево данных и метаданных. Только, чтобы избежать круглых скобок, создатели SGML/HTML/XML придумали использовать угловые и записывать название каждого тэга дважды! HTML элементарно преобразуется в подмножество Lisp’a, как показывают многочисленные реализации Lisp’овых библиотек для HTML-генерации (например, CL-WHO или ParenScript, который также реализует JavaScript и CSS). Интересно то, что в Lisp’е HTML-генерация намного естественней заточенного специально под задачу создания динамических веб-страниц PHP, в котором все в конце концов сводится к банальному вызову функции echo.&lt;br /&gt;&lt;br /&gt;За счет использования MACRO’в, Lisp позволяет выражать свои мысли в наиболее лаконичной и адекватной форме, а каждый программист имеет способы самостоятельно сформировать набор примитивных конструкций языка, с которыми он будет работать в рамках конкретной проблемы, программы или платформы. Вы можете сказать, как и в большинстве других языков if ... then ... else ..., но также вы можете сказать when … then …, unless ... then ...; кроме того, вы можете сказать condition 1 -&gt; expression 1; ... condition n -&gt; expression n, case ... variant 1: expression1 ... variant n: expression n. И это далеко не полный список, на самом деле, вы можете легко реализовать любую логическую конструкцию и по возможностям использования в программах она ни чем не будет отличаться от обычного if-then-else.&lt;br /&gt;&lt;br /&gt;Конкретный пример: в стандарте Lisp есть 3 вида case конструкций, которые имеют такую обобщенную форму:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;    case-конструкция тестовый элемент&lt;br /&gt;    (значение1 форма1)&lt;br /&gt;    ...&lt;br /&gt;    (значениеN формаN)&lt;br /&gt;    [(otherwise формаN+1)]&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Они отличаются тем, что обычная case-конструкция имеет default clause, а 2 другие: ecase и ccase, — выдают ошибки в случае ненахождения значения тестового элемента в списке возможных. Ограничением этих конструкций является использование для сравнения функции eql (опять же одной из множетсва операций, которые используются для проверки на равенство). Эта функция не работает для строк, поэтому я решил написать собственный вариант case, который бы давал возможность использовать любую функцию сравнения. Например, эта может быть функция, которая проверяет нахождение числа в рамках заданного интервала. Хочу обратить внимание на то, что конструкция gcase объявлена мной совершенно таким же образом, как и оригинальные case-конструкции — а возможность узнать их реализацию, не залезая глубоко в код или описание стандартных библиотек (доступа к которым может и не быть), как это делается в других языках, можно, как правило, просто запустив macroexpand.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;(defmacro gcase ((keyform &amp;key (test #'eql)) &amp;body clauses)&lt;br /&gt;  "GENERALIZED-CASE -- the difference from simple CASE is that it can&lt;br /&gt;use any given TEST-function. TYPE-ERRORs signaled by TEST-functions are ignored"&lt;br /&gt;  (unless (listp clauses) (error "~a -- bad clause in CASE" clauses))&lt;br /&gt;  (let ((t-clause? nil))&lt;br /&gt;    (when (eql (caar (last clauses)) 'otherwise)&lt;br /&gt;      (setf t-clause? t))&lt;br /&gt;    `(let ((it ,keyform))&lt;br /&gt;      (cond&lt;br /&gt; ,@(mapcar #'(lambda (clause)&lt;br /&gt;        (if (and t-clause? (eql (car clause) 'otherwise))&lt;br /&gt;     `(t ,@(cdr clause))&lt;br /&gt;     (w/uniqs (c)&lt;br /&gt;       `((handler-case (funcall ,test it ,(car clause))&lt;br /&gt;    (type-error (,c) (warn "The value ~a is not of type ~a"&lt;br /&gt;                           (type-error-datum ,c)&lt;br /&gt;                           (type-error-expected-type ,c))))&lt;br /&gt;         ,@(cdr clause)))))&lt;br /&gt;    clauses))))) [&lt;a href="#5"&gt;5&lt;/a&gt;]&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;То же самое касается любых других абстракций. Людям, привыкшим работать в жестко ограниченных языках, не понятно, зачем иметь столько конструкций для циклов: do, dotimes, dolist, loop, …,— к которым могут быть дописаны еще какие-нибудь dotree, domap и т.п. (Хотя многие, наверно, вспомнят, что были рады увидеть, наконец, в C# конструкцию foreach, которой так не хватало в С++). В действии все тот же принцип хорошего стиля — писать все только один раз: во-первых, не нужно копи-пейстить одни и те же шаблоны снова и снова, во-вторых, сразу понятно, какого типа цикл перед нами (а не так, как в С: for (;;)), в-третьих, легко можно уточнить и расширить эти конструкции с помощью инструментов такого же типа (все эти операции циклов — тоже MACRO)...&lt;br /&gt;&lt;br /&gt;Почему макро-систем нет в других языках? На самом деле они были и есть: Dylan — это Lisp с С-синтаксисом, metalua и пр. Очевидно, что для поддержки макров язык должен иметь возможность задействовать позднее связывание, но главная проблема остается в другом: разнородности и запутанности синтаксиса — в трудности учета самим программистом всех возможных побочных эффектов “скрытия” каких-то частей кода. Не то, чтобы такая система была не возможна — она, просто, не так практична, потому что появляется необходимость дополнительной трансляции кода между разными представлениями в воображении. А для полноценного использования макров, как показывает даже простой пример вверху, часто не удается ограничиться только одним уровнем вложения макрокода.&lt;br /&gt;&lt;br /&gt;Но и без макро-системы трудно говорить о полноценном языке программирования. Мне кажется, это остановка на полпути для программиста — главная задача которого автоматизировать выполнение операций людьми с помощью компьютера — не иметь возможности или не научиться автоматизировать собственные операции.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;5. Обещание Lisp’а&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Большинство книг и введений в Lisp обещает, что изучив его, программист станет другим, лучшим. Вряд ли это так, потому что никого нельзя изменить при отсутствии собственного желания измениться (и всегда можно будет найти объяснение отсутствию желания, будь то “наличие/отсутствие скобок, переменных, объектов, …”, отсутствие библиотек, необходимость кормить семью…). На самом деле, главное обещание Lisp’а в другом — в том, что человек сможет выражать свои мысли при программировании свободно, в любой принятой им парадигме или вне парадигм без необходимости руководствоваться искуственными ограничениями. Идея, которая стоит за Lisp’ом сегодня — ясность. Но, как и за все остальное, за нее приходится чем-то расплачиваться. И, как по мне, пусть это что-то будет парой лишних скобок... :-)&lt;br /&gt;&lt;br /&gt;Сноски:&lt;br /&gt;&lt;a name=1&gt;[1]&lt;/a&gt; На самом деле, давайте не будем беспокоиться о Javе. Давайте не жаловаться на Microsoft. Давайте не беспокоиться о них, потому что мы тоже знаем, как программировать компьютеры, и, на самом деле, мы знаем, как делать это мета путем. Мы можем установить альтернативную точку зрения и мы не единственные, кто делает так, как вы хорошо знаете&lt;br /&gt;&lt;a name=1&gt;[2]&lt;/a&gt; Есть большая разница между работой с языком, в котором вы должны ждать год, чтобы получить новое выражение для цикла от дизайнера языка и реализатора компилятора — или, когда вы можете сделать это сами за 20 минут&lt;br /&gt;&lt;a name=1&gt;[3]&lt;/a&gt; First, we want to establish the idea that a computer language is not just a way of getting a computer to perform operations but rather that it is a novel formal medium for expressing ideas about methodology. Thus, programs must be written for people to read, and only incidentally for machines to execute.&lt;br /&gt;&lt;a name=1&gt;[4]&lt;/a&gt; http://www.defmacro.org/ramblings/lisp.html&lt;br /&gt;&lt;a name=1&gt;[5]&lt;/a&gt; В этой реализации, конечно, есть не только скобки, но и специфические функции, а также несколько синтаксических элементов (называемых синтаксическим сахаром): ' ` , #' ,@. На самом деле, это практически исчерпывающий список всех подобных элементов в Common Lisp, более того все они имеют свои аналоги в форме (имя ...), т.е., в конце концов, можно ограничиться только скобками и именами.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-8256630332797579789?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/8256630332797579789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=8256630332797579789' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8256630332797579789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/8256630332797579789'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/07/blog-post_25.html' title='О пользе унификации и вреде предубеждений'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-3356229790468654742</id><published>2008-07-25T15:07:00.005+03:00</published><updated>2008-07-25T15:41:27.849+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ua'/><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><title type='text'>Дилема зеків на київських дорогах</title><content type='html'>&lt;span id="for-and-date"&gt;&lt;strong&gt;Написано для:&lt;/strong&gt; Украинская правда (не опубликовано)&lt;br /&gt;&lt;strong&gt;Время написания:&lt;/strong&gt; январь 2008&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Є такий важливий результат теорії ігор &amp;mdash; &lt;a href="http://uk.wikipedia.org/wiki/%D0%94%D0%B8%D0%BB%D0%B5%D0%BC%D0%B0_%D0%B2'%D1%8F%D0%B7%D0%BD%D1%8F"&gt;дилема в'язнів&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Коротко, двоє підозрюваних, А і Б, арештовані. У поліції немає достатніх доказів для звинувачення, і ізолювавши їх один від одного, вони пропонують їм одну і ту ж операцію: якщо один свідчить проти іншого, а той зберігає мовчання, то перший звільняється, а другий одержує 10 років в'язниці. Якщо обидва мовчать, у поліції мало доказів, і вони засуджуються до 6 місяців. Якщо обидва свідчать проти один одного, вони одержують по 2 роки. Кожен ув'язнений вибирає, мовчати або свідчити проти іншого. Проте жоден з них не знає точно, що зробить інший. Що відбудеться?&lt;br /&gt;&lt;br /&gt;Математично доведено: якщо кожний зі злодіїв буде діяти виключно у своїх (егоїстичних) інтересах, тобто, виражаючись формальною мовою, раціонально, обидва будуть говорити і тому отримають по 2 роки. Чому? Це легко пояснити. Візьмемо одного зі злодіїв. Якщо другий буде говорити, то і йому вигідно те ж саме, бо він отримає тільки 2 роки, а не 10. Якщо ж другий буде мовчати, то і тут вигідно співпрацювати з поліцією, бо замість півроку у в'язниці наш злодій одразу виходить на свободу.&lt;br /&gt;&lt;br /&gt;Як бачимо, діючи скоординовано, ці двоє отримали б менший строк, ніж діючи кожний виключно у своїх інтересах. Це пов'язано з тим. що, виражаючись математичною мовою, ця гра &amp;mdash; не з нульовою сумою.&lt;br /&gt;&lt;br /&gt;Цікаво... А тепер порівняйте це з ситуацією на київських вулицях, особливо під час пробки. Деякі люди, бачачи пробку, думають: "зараз я швиденько прорвуся по порожній зустрічній смузі", "виїду на червоне світло, бо ці бовдури з перпендикулярної вулиці затримались на перехресті більше належного" і так далі. А виходить, що це тіьки погіршує становище. Мабуть, тому, що на кожного хитрого знайдеться ще хитріший, а на кожного зухвалого &amp;mdash; ще зухваліший.&lt;br /&gt;&lt;br /&gt;Так що, певне &amp;mdash; напрошується питання &amp;mdash; у більшості киян зеківське світосприйняття? :)&lt;br /&gt;&lt;br /&gt;Добре, окрім жартів, чи є вихід з такої ситуації? Ну, як мінімум є 2 виходи: простий і складний. Складний полягає в тому, щоб всім домовитись між собою про певні "правила гри", усні чи письмові, або навіть прийняти закони і створити систему забезпечення їх дотримання.&lt;br /&gt;&lt;br /&gt;Є ще простий вихід, який, мабуть, здасться безглуздим хитрому лису, про якого пише Ніко Ланге у своїй статті "&lt;a href="http://www.pravda.com.ua/news/2007/12/5/67911.htm"&gt;Україна не є правовою державою&lt;/a&gt;". Справа в тому, що для людей з релігійною (або навіть просто моральною) свідомістю дилеми в'язнів взагалі не існує, бо кожна релігія вчить поводитись по відношенню до оточуючих так, як ви хочете, щоб поводились по відношенню до вас. Це значить, наприклад, пропустити іншого попереду, бо ти хочеш, щоб і тебе пропустили , коли ти поспішаєш; не лізти поперед іншими, бо і тобі не подобається, коли перед тобою влізають... Виявляється, навіть математика говорить, що це, може, й не такий поганий варіант.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-3356229790468654742?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/3356229790468654742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=3356229790468654742' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3356229790468654742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/3356229790468654742'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/07/blog-post.html' title='Дилема зеків на київських дорогах'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-6497564703594709691</id><published>2008-07-16T07:03:00.001+03:00</published><updated>2008-07-16T19:34:19.655+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>Мысли по поводу ICFPС '08</title><content type='html'>&lt;h3&gt;1. Забавные факты о нашей команде&lt;/h3&gt;&lt;br /&gt;Изначально наша команда состояла из 4-х человек:&lt;br /&gt;- двоих из Питера: того, кто нас собрал -- Якова Зайцева (lj:iakovz), -- выбравшего Erlang, и его друга с C++&lt;br /&gt;- питонщика из Омска&lt;br /&gt;- меня с Common Lisp&lt;br /&gt;(Более распределенной и полиглотной, наверно, трудно представить. Впрочем, мы считали, что наш основной язык -- Lisp).&lt;br /&gt;&lt;br /&gt;В этом составе мы провели тренировку...&lt;br /&gt;&lt;br /&gt;Когда началось соревнование, нас было (потенциально) 6: к нам присоединился друг нашего Омского товарища и Эндрю Бейн -- программист на Lisp'е, который искал Lisp'овую команду в списке рассылке.&lt;br /&gt;&lt;br /&gt;...А когда мы отправляли, наша готовая программа состояла из 2=х модулей: низкоуровневого на Erlang'е, отвечавшего за маневрирование, написанного Яковом, и моего высокоуровневого для выбора направления. Кроме того, в СКВ была куча C++ кода (не интегрированного с нашим), написанного парнем из Омска, который присоединился последним, в день соревнования. Я не отправлял ее, поскольку мы не знали, что там к чему...&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;2. Коммуникация&lt;/h3&gt;&lt;br /&gt;Как по мне, нашей главной проблемой было взаимодействие (и, в некоторой степени, лидерство, поскольку среди нас не было авторитета, который бы разрешил все несогласованности): мы не смогли договориться о разделении задач, поэтому каждый фактически делал то, что хотел, разве что не считая нас с Яковом. Эндрю пытался поучаствовать, но не смог, как мне кажется, из-за плохого взаимодействия и большой разницы во времени. Мы были русско-говорящей командой и готовились вместе, а он присоединился к нам поздно ночью, когда мы уже многое обсудили. Так что у нас не было времени вовлечь его в процесс вначале, а на следующий день ему было еще труднее начать.&lt;br /&gt;&lt;br /&gt;Но, что касается нашей пары, с ней тоже произошла забавная история с коммуникацией: в последний вечер мы отлаживали часть Якова во в некотором роде "распределенной" манере: он делал исправление, заводил его в SVN, я забирал его, отправлял через scp на машину с LiveCD, запускал, отлавливал в консоли ошибки, сохранял в текстовый файл, scp назад на свой ноутбук и отправлял Якову по Skype. Она продолжалась, мне кажется, часа 3 и, что интересно, в конце я смог отправить почти работающую программу. :) Это произогло из-за того, что у Якова на работе были какие-то неполадки с интернетом, поэтому он не мог загрузить liveCD, а я оказался настолько тупым, что не подумал запускать клиента по сети...&lt;br /&gt;&lt;br /&gt;Но, с точки зрения технической, Skype -- это, реально, практически совершенное решение для коммуникации в распределенных группах.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;3. Минимальный набор инструментария&lt;/h3&gt;&lt;br /&gt;который бы понадобился вам в этом году для участия в составе гео-распределенной команды:&lt;br /&gt;* groupchat&lt;br /&gt;* SCM в сети -- GoogleCode доя нас сработал отлично&lt;br /&gt;* отдельный комп для запуска liveCD&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;4. Задание&lt;/h3&gt;&lt;br /&gt;Задание этого года -- это как раз такое задание, которое я бы давал студентам в качестве курсовой работы по дисциплине, которая рассматривает основы программирования и алгоритмы. С одной стороны, оно учит большинству умений, необходимых чтобы начать работу в профессиональной группе программистов, а с другой, оно достаточно сложное, чтобы действительно научить чему-то об алгоритмах. Мы также пробовали задание 2004 года -- Муравьев -- оно тоже похоже в этом смысле, но все же больше подходит для курса по компиляторам. В целом, если бы я был преподователем программирования, я бы однозначно использовал материалы всех ICFP соревнований для подобных задач.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;5. Чего мне не хватало&lt;/h2&gt;&lt;br /&gt;Я думаю, для меня наилучшим вариантом было бы участвовать в команде из 4-5 человек, находящихся не более чем в двух разных местах (большая разница во времени между которыми была бы очень на руку), и программирующих не более, чем на 2-х языках, чтобы у каждого был напарник для работы над кодом вместе. И было бы отлично, если бы каждый знал оба языка.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Вывод:&lt;/h3&gt;&lt;br /&gt;Это было отличное времяпровождение, которое я бы посоветовал попробовать хотя бы раз в жизни каждому, кто любит программировать.&lt;br /&gt;...И в идеале, конечно, нужно участвовать в составе хорошо подгтовленной команды :)&lt;br /&gt;&lt;br /&gt;P.S. &lt;a href="http://bp3.blogger.com/_nJPGaVA_wis/SHz-hc8UH1I/AAAAAAAAALA/bcnzKITuwEs/s1600-h/submission.png"&gt;Наш сабмишн.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-6497564703594709691?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/6497564703594709691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=6497564703594709691' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6497564703594709691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/6497564703594709691'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/07/icfp-08_16.html' title='Мысли по поводу ICFPС &apos;08'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-4838927636364593842</id><published>2008-07-15T18:31:00.011+03:00</published><updated>2008-07-25T22:15:46.589+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='en'/><title type='text'>ICFPC '08 Thoughts</title><content type='html'>&lt;h3&gt;1. Funny things about our team&lt;/h3&gt;&lt;br /&gt;Our team consisted initially of 4 people:&lt;br /&gt;- 2 guys in St.Petersburg: the guy, who've brought us together -- Yakov Zaytsev (lj:iakovz) -- choosing Erlang, and his friend with C++&lt;br /&gt;- a pythonista in Omsk (Siberia, Russia)&lt;br /&gt;- me with Common Lisp&lt;br /&gt;(As distributed and polyglot, as you can get. Though, we consider Lisp to be our main language).&lt;br /&gt;&lt;br /&gt;We had a training session with this roster...&lt;br /&gt;&lt;br /&gt;When the contest began, there were (potentially) 6 of us: we were joined a friend of our Omsk teammate and Andrew Baine -- a Common Lisp hacker, who asked in the mailing list to join a Common Lisp team.&lt;br /&gt;&lt;br /&gt;...And when we submitted, our final program consisted of 2 modules: a low-level Erlang one for maneuvering by Yakov and my high-level part for direction setting. Besides in the SCM there was a heap of C++ code (not integrated with ours), written by the Omsk guy, who appeared last. (I hadn't submit it for we didn't know, what was it all about)...&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;2. Communication&lt;/h3&gt;&lt;br /&gt;As for me, our main problem was communication (and, to some extend, leadership  -- there was no outstanding authority among us to resolve all the disagreements): we couldn't agree on the division of tasks, so everyone virtually did, what he wanted, except for us with Yakov. Andrew tried to participate, but didn't succeed because of poor communication and big time difference, I think. We were Russian-speaking and prepared together and, moreover, he joined, when we've being already far in the discussion late at night. So, we didn't have enough time, to get him involved in the beginning, and on the next day it was even harder to start for him.&lt;br /&gt;&lt;br /&gt;But with our pair it was also a funny story with communication: on the final evening we debugged Yakov's part in a somewhat "distributed" manner: he made a correction to his module, committed it to SVN, I checked it out, scp'ed to the machine with LiveCD, ran, caught errors popping in the console, wrote them to text file, scp'ed back to my notebook and sent to Yakov over Skype. This debugging session lasted for some 3 hours I think, and actually, I was able to submit some half-working code in the end. :) Such situation was caused by Yakov being at work with poor Internet connection, so he couldn't get hold of liveCD, and me being stupid enough not to think of running the client over the net...&lt;br /&gt;&lt;br /&gt;But, from the technical viewpoint, Skype, actually, is as close to perfect as you can get for communication in such distributed groups.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;3. Minimum set of tools&lt;/h3&gt;&lt;br /&gt;you would have needed for participating this year with a geo-distributed team:&lt;br /&gt;* groupchat&lt;br /&gt;* SCM on the net -- GoogleCode worked perfectly for us&lt;br /&gt;* a separate PC to run liveCD&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;4. Task&lt;/h3&gt;&lt;br /&gt;This year's task is just a kind of a task to give students for their term paper in the course, that teaches basic programming and algorithms. From one perspective, it would teach you most of the things you need to know to start working in a professional group of programmers, and from the other it's sufficiently advanced to really learn something about algorithms. We've also tried the 2004's task -- Ants -- it's also similar in this sense, but is more suited for the Compiler course. Generally speaking, if I was a programming professor, I would have definitely used the materials from all the ICFP contests as such kinds of tasks.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;5. What I lacked&lt;/h3&gt;&lt;br /&gt;I think, for me the best case would have been to have a team of 4-5 people, situated in at most 2 places (and big time difference between them would've been really helpful), and programming in at most 2 different langs, so that everyone had a supporting person to work on the code together. And, it would've been great if everyone knew both languages.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Conclusion:&lt;/h3&gt;&lt;br /&gt;It was really a great experience, which I'd recommend to try at least once to anyone, who is fond of programming.&lt;br /&gt;...And it's best, when you are in a well-prepared team :)&lt;br /&gt;&lt;br /&gt;P.S. &lt;a href="http://bp3.blogger.com/_nJPGaVA_wis/SHz-hc8UH1I/AAAAAAAAALA/bcnzKITuwEs/s1600-h/submission.png"&gt;Our submission.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6031647961506005424-4838927636364593842?l=lisp-univ-etc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lisp-univ-etc.blogspot.com/feeds/4838927636364593842/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6031647961506005424&amp;postID=4838927636364593842' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/4838927636364593842'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6031647961506005424/posts/default/4838927636364593842'/><link rel='alternate' type='text/html' href='http://lisp-univ-etc.blogspot.com/2008/07/icfpc-08-thoughts.html' title='ICFPC &apos;08 Thoughts'/><author><name>Vsevolod Dyomkin</name><uri>http://www.blogger.com/profile/07729454371491530027</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6031647961506005424.post-2982003276722829163</id><published>2008-06-24T11:38:00.007+03:00</published><updated>2010-07-06T10:14:24.676+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='universe'/><category scheme='http://www.blogger.com/atom/ns#' term='ru'/><title type='text'>ПИФы и экономика украинского фондового рынка</title><content type='html'>&lt;span id="for-and-date"&gt;&lt;strong&gt;Написано для:&lt;/strong&gt; журнала Эксперт-Украина (не опубликовано)&lt;br /&gt;&lt;strong&gt;Время написания:&lt;/strong&gt; сентябрь 2007&lt;/span&gt;&lt;br /&gt;&lt;h2&gt;Некоторые аспекты инвестирования, о которых не все задумываются&lt;/h2&gt;&lt;br /&gt;Тема украинского фондового рынка (УФР) интересовала меня давно, но его "участником" я стал только в августе этого года, и смог многие моменты, что называется, на собственном опыте прочувствовать намного лучше, чем в то время, когда был лишь сторонним наблюдателем. В этой стат5ье изложены мои субъективные оценки перспектив "инвестирования" в УФР для частных лиц – граждан Украины.&lt;br /&gt;&lt;br /&gt;Почему "инвестирования" в кавычках? Среди базовых форм экономической деятельности, которыми являются:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;создание и/или распределение ценностей (производство, продажа);&lt;/li&gt;&lt;li&gt;потребление ценностей (покупка);&lt;/li&gt;&lt;li&gt;посредническая деятельность (эксплуатация транзакционной неэффективности экономики);&lt;/li&gt;&lt;li&gt;инвестирование (вложение средств/ресурсов для получения большего возврата в будущем)&lt;/li&gt;&lt;/ul&gt;в настоящий момент многие люди рассматривают инвестирование как "серебряную пулю", способную обеспечить достаточный доход относительно легко и в более короткие сроки (по сравнению с обычной работой, которая является, как правило, первой из форм хозяйственной деятельности). Но нужно учитывать, что как и работа клерка отличается от работы директора предприятия, так и в инвестировании разные его формы требуют разных качеств, разных форм вложений (не только денег, но и времени, усилий, умений и пр.) и дают разную отдачу.&lt;br /&gt;&lt;br /&gt;Фактически к инвестированию сейчас принято относить деятельность двух существенно разных направленностей, определяемых тем, каким образом будет получен возврат от вложений:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;вложение по принципу "купи подешевле – продай подороже";&lt;/li&gt;&lt;li&gt;вложение для получения доходов разными порциями в течение долгого промежутка времени&lt;a href="http://www.blogger.com/post-edit.g?blogID=6031647961506005424&amp;amp;postID=2982003276722829163#pif1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Разница между инвесторами первого и второго типа в первую очередь во временном горизонте инвестирования. При том, что оба ищут высокие прибыли, первый надеется получить их просто за счет грамотного выбора времени входа на рынок и выхода из него, т.е. он пытается "обыграть" рынок благодаря своим уникальным качествам или доступной ему уникальной информации. Но, если учесть то, что действительно успешным спекулянтам приходиться работать по 14-16 часов в сутки без выходных при всех их способностях, знаниях и опыте, то на ум приходит параллель между инвестором, ожидающим быстро и легко достичь успеха таким путем, с игроком в казино (в наличии те же особенно
