Caution: my English is far from perfect. (Русский тоже не всегда хорош).


Saturday, 1 May 2021

Stackoverflow cookies

Stackoverflow and related sites repeatedly display their cookie confirmation dialog.

So many times I had to press "Customize settings", and there select "Confirm my choices".


This time I mistakenly pressed "Accept all cookies".

How to undo that? Why do they show it every time? Will they continue showing it, or it only annoys you until you press "Accept all cookies"?

Monday, 25 March 2019

- Что делал слон, когда пришел Наполеон?
- Уху ел.

Friday, 21 July 2017


копичатка - мелкая ошибка, сделанная в результате копи-паста. Напрмер, забыли заменить всё, требующее замены.

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

Monday, 26 September 2016

Partitioned, Available and Consistent?

A database storing a bank account with balance $100 is replicated to two nodes. A network partition happens between the nodes. I am withdrawing $20 cash from an ATM which has access to a replica on one side of the partition. At the same moment I'm charged $10 for hosting and the payment gateway has access the replica on the other side of the partition.

That's OK, the DB nodes process the withdrawal as long as the amount is under a half of the balance at the time of their last synchronization. (Each node can spend up to $50).

Does it qualify as simultaneous availability, consistency and partition tolerance?

Account log says, depending on the node you're connected to, either:

4. final balance: between $30 and $80
3. spent $20
2. network partition 50 / 50
1. initial balance: $100


4. final balance: between $40 and $90
3. spent $10
2. network partition 50 / 50
1. initial balance: $100

Thursday, 10 March 2016

Мчится тройка удалая,
Тройка сталинская

Friday, 2 October 2015

Новости международной политики

(Предлагаю смотреть 40 секунд начиная с этого места.)

Saturday, 5 September 2015

Imperative to Functional

How to automatically transform any imperative code into a functional code?

Think for a moment before opening the answer: >> <<

Copy all the data before invoking the imperative code.

That's not a joke. There are techniques (e.g. copy on write) and data structures which can make it efficient. I have no time to write-down all the existing analogies coming to my mind now. What I want to say, the imperative and functional approaches in many important aspects are not that different. We can think of imperative assignment to a variable as of pure function which computes new world where this variable has new value.

Wednesday, 12 August 2015

Javascript: LexicalEnvironment vs VariableEnvironment

One javascript feature I learned while working on POCL.


function foo() {
    try {
        throw 'hello'
    } catch (e) {
        return function inner() {return e}

=> 'hello'

function foo2() {
    try {
        throw 'hello'
    } catch (e) {
        function inner() {return e}
        return inner;

Uncaught ReferenceError: e is not defined

Why do the results differ?

In the first case INNER is a function expression, while in the second case INNER is a function declaration.

According to the standard:

Javascript execution context has two Lexical Environments. One is called LexicalEnvironment (he-he), and another is VariableEnvironment [§10.3].

The CATCH statement introduces new LexicalEnvironment where E is defined, and leaves the VariableEnvironment untouched.

Function expression receives current LexicalEnvironment as its scope, while function declaration receives current VariableEnvironment as its scope [§13], therefore E is invisible for the function declaration.

Why everything is so complicated, why two Lexical Environments exist?

My guess it's because we can call function before its declaration:

(function () {
    return inner();
    function inner() {return 1};

=> 1

(function () {
    var x = inner();
    return x;
    function inner() {return 1};

=> 2

(function () {
    var x = inner();
    try {
       throw 'error'
    } catch (unused) {
       function inner() {return 1};
    return x;

=> 1;

To support this behavior, the variable INNER is created and bound to the function object early, when control enters the surrounding function; before any CATCH is executed; and therefore it doesn't see the variable introduced by the CATCH clause.

Unlike normal variables, visible in the scope of the whole enclosing function, the variable introduced by CATCH is only visible inside the CATCH clause:

(function() {
    var x = 1;
    try {
        throw 2
    } catch (x) {

// log output:

That's why (I guess) javascript needs to distinguish LexicalEnvironment and VariableEnvironment.

During years of JS programming I never hit this difference in practice - never captured a catch variable by a closure. I only detected this strange thing when reading the standard in order to implement some code transformations needed for POCL, and was wondering, why does it need to distinguish LexicalEnvironment from VariableEnvironment.

BTW, another case when names are bound within a scope smaller that the enclosing function is the WITH statement. So it has the same problem regarding function expression / function declaration:

var o = {x: 1, y: 2}
(function() {with (o) {return function inner() {return x}}})()()
=> 1

(function() {with (o) {function inner() {return x}; return inner}})()()
Uncaught ReferenceError: x is not defined

Friday, 7 August 2015

Predictive Optimizing Code Loading

An idea I kept in mind for several years. Finally experimented with it:

I consider the experiment successful. It could be a useful technique of application acceleration.

Saturday, 20 December 2014

Web Design Advice

Don't just use gray text on white background. If you really want to make your text difficult to read, you will achieve even better results with white text on white background.

Upd: I am not alone who thinks so:

Friday, 22 August 2014

Semantic Versioning is Not the Solution

People often think they can introduce incompatible changes in their library API, and just increase major version number, as semantic versioning proposes, to save the library clients from problems.

It is not true.

Consider a dependency tree:
  web-server 1.1.1
    commons-logging 1.1.1
  db-client 1.1.1
    commons-logging 1.1.1
  authentication 1.1.1
    commons-logging 1.1.1
Now commons-logging changes its API incompatibly and is released as commons-logging 2.0.1. Authentication adopts commons-logging 2.0.1 while other libraries still depend on 1.1.1:
  web-server 1.1.1
    commons-logging 1.1.1
  db-client 1.1.1
    commons-logging 1.1.1
  authentication 1.1.2
    commons-logging 2.0.1
Now my-application is broken, because the dependency tree includes two versions of commons-logging which share packages, class/functions names, and thus can not be loaded simultaneously.

When you release an incompatible API this way, you essentially split the world of dependent libraries into two parts: the ones depending on the old version, and ones depending in new version. Libraries from the first part can not be used together with libraries from the second part.

A better way to introduce incompatible API is to release it as a new library, for example commons-logging2, or new-logging. Make it possible to use the new library simultaneously with the old one, e.g. it should have new package name.
Doing so will protect clients in majority of cases.

If we are releasing new library for new API, there is no need for such a thing as "major version number".

NB: in some module managers, most notably in javascript, there are no global package/class names on which different versions of a library can interfere. But in majority of programming languages that problem exists.

Saturday, 25 January 2014

Какие похожие картинки. Очень надеюсь, что до похожего результата дело не дойдет.

Monday, 20 January 2014

How many more years it will take Chrome and FireFox to learn display text properly?

These are original size screenshots of some page heading.
I think such bad results may be achieved if Chrome and FireFox rasterize first and scale after that, instead of scaling first and then rasterizing.
To reproduce, here is the code: <h1 style="font-size: 300%; color: #4f81bd; font-style: italic; font-family: 'Times New Roman', Times, serif;">WINTER ACTION</h1>

Update: Many people reported FireFox and Chrome render fonts OK for them. HN comment and tnm91 suggested that DirectWrite may be disabled, due to outdated video driver. It was true, I updated diver and DirectWrite become enabled, but rendered hadn't changed. Then sulliwan suggested to check CliearType - I enabled it and now FireFox and Chrome render text OK.

Thanks for the help!

Monday, 23 September 2013

issue 9999

Have just opened another GAE issue, and it got number #9999
It's pretty. The next reporter will get number 10000.

Friday, 3 May 2013

Указ 60 и ЕЭП

Давно думаю, не противоречит ли указ №60 принципам Единого экономического пространства Беларуси, России и Казахстана?

Вроде у нас свобода торговли, и вдруг раз - на территории Беларуси при оказании услуг с помощью информационных систем могут применяться только информационные сети, системы и ресурсы "национального сегмента сети Интернет, размещенных на территории Республики Беларусь и зарегистрированных в установленном порядке". Значит ли это, что например Российским системам, провайдерам хостинга и т.п. вход на Белорусский рынок закрыт?

Sunday, 14 April 2013

Github support for org-mode in READMEs is unsatisfying

Github support for org-mode in READMEs is unsatisfying - no code coloring and sometimes it even fails to render to html at all.

I am migrating all my README files to markdown. Googled also this tool for automatic conversion: Will try it next time.

Apart from READMEs I also would like to represent files online. While org -formatted READMEs are more or less readable in gitbub's HTML representations, files are not readable at all.

Saturday, 9 February 2013

Quest for Immutable Static Files Hosting

For cl-test-grid I need an online storage for static files - library test logs.

The requirements:

  1. Permissions:
    • Upload new file - anyone (because the tests may be run by anyone)
    • Delete or modify existing file - forbidden for public.
  2. Current upload rate is around 50 000 small files per month, usually uploaded during several days after new Quicklisp is out. In the future it may increase to 100 000 or more. Size of each file is <= 100 KB.

After file is uploaded it must be available by an HTTP URL.

None of the online file storage services I saw satisfy my permissions requirements. Usually if user can upload file, he can delete/modify it. Therefore every of the solutions below include an intermediate server application which enforces my access rules.

I have considered several variants:

  1. Google App Engine Blobstore

    The first solution I found and it served me for more than a year. The free usage quota includes 5 GB of blobstore, which I hoped will be enough for years.

    But it shown some disadvantages. I faced some bugs: #7619, #8032. Also every blob upload takes several Datastore operations, thus exhausting the free Datastore quota. Also the blob keys are 164 characters long, making the log URLs horrible:

    To make the URLs shorter I save blobkey in a Datastore Entity, and use the integer entity ID as a reference to the log:

        Entity shortKeyEntity = new Entity("ShortKey");
        shortKeyEntity.setProperty("blobKey", blobKey);
        return shortKeyEntity.getKey().getId();
    That way the URLs look better: But this increases amount of Datastore operations. Also, the Datastore creates some internal indexes for the entities, and which take huge amount of space. I in total have 1GB of log files, but it takes 4GB of Datastore to keep the mapping from blob keys to short IDs:

    So my application exceeds the free quota by several parameters and the paid plan must be used. It is > $108 per yer. Expensive for just publishing 1GB of static files.

  2. Google App Engine + Google Cloud Storage

    Cloud Storage is an Amazon S3 clone. I migrated to it from App Engine Blobstore recently.

    As you choose file names yourself there is no problem with long keys. Also Cloud Storage doesn't involve any App Engine Datastore operations.

    There is an API to access it directly from App Engine, although it is experimental. And I faced a bug #8592; solved it by a work-around.

    Other difficulties to solve were:

    • Handling of multipart/form-data based file uploads, because Google App Engine does not support servlet API 3.0 yet. And Apache Commons FileUpload library doesn't work out of box in App Engine, because it tries access file system.
    • Java Servlets on App Engine should handle every request in 30 seconds time-out. The servlet is killed if exceeds the time-out.
    • Writing files one-by-one to Cloud Storage via the Java API is relatively slow.

    Having solved all this by customizing Apache Commons FileUpload to keep the uploaded files in memory while parsing the request body and writing to Cloud Storage from multiple threads I now have a solution allowing to upload 300 logs in one request. And with the free 5GB provided by Google for the fist Cloud Storage project, the payments for this service will most likely be zero.

  3. Amazon S3 + Heroku.

    This is the next thing I'll do if I have any problems with CloudStorage. A small app at Heroku creating pre-signed S3 URLs and returning them to client. The client uploads files and after the pre-signed URLs expire he can not modify the files.

    Amazon only allows to upload one file per PUT request. With the price of $0.01 for 1000 requests it may cost me around $10 a year.

    A tempting property of this solution is that the Heroku app may be written in Common Lisp thus getting rid of Java servlets in cl-test-grid.

Tuesday, 5 February 2013

Multicore Forth processors provide Go-like concurrency primitives in hardware

In the Go Concurrency Patterns presentation Rob Pike demonstrates how unbuffered channels are
enough for many concurrency tasks. (NB: use Left/Right arrow keys to scroll the presentation)

It reminded me of the Forth chips produced by Chuck Moore and colleagues.

The current version contains 144 cores per square centimeter chip. The machine language is Forth and
each core is equipped with its own little data and control Forth stacks, making it a fully fledged
independent computer (that's why the more precise term is "multi-computer chips" rather than "multi-core").

The cores talk to each other via communication ports. Writing to a port suspends the core until
the peer reads the value. And vice-versa.

This semantics corresponds to the Go channels.

The chips have other interesting properties. Quoting the doc:

A computer can read from multiple ports [Corresponds to Go's select]
and can execute instructions directly from those ports.

FINE GRAINED ENERGY CONTROL: ... The read or write instruction is automatically 
suspended in mid-operation if the address [one or more of communication ports and I/O pin] is inactive,
consuming energy only due to transistor leakage currents, resuming when the address becomes active. 

NO CLOCKS: Most computing devices have one or more clocks that synchronize all 
operations. When a conventional computer is powered up and waiting to respond 
quickly to stimuli, clock generation and distribution are consuming energy at a huge rate 
by our standards, yet accomplishing nothing. This is why “starting” and “stopping” the 
clock is a big deal and takes much time and energy for other architectures. Our 
architecture explicitly omits a clock, saving energy and time among other benefits.

Read more at the company website.

As Rob Pike says, the channel-like concurrency primitives are not new. It is interesting to see
them implemented in hardware.

Monday, 3 September 2012

Linux memory over-commit.

Linux memory over-commit. Previously I've only read about it, but now I am bitten by it. When cl-test-grid agent (a CCL program) starts ECL and ECL starts gcc, linux on my VPS kills someone of them (usually CCL). Sometimes it just doesn't allow to fork new process. Why do CCL and other Lisps allocate so large heap at startup?

Tuesday, 17 January 2012


I once made a small invention: the simplest way to join values in 
a collection using comma as a separator:

    result = '';
    maybeComma = '';
    for (val : collection) {
      result += maybeComma + val;
      maybeComma = ',';

If collection = [1, 2, 3], then result = '1,2,3'.

I invented this almost 10 year ago, and it was useful many times. There are 
environments, like shell scripts, where there is no Arrays.toString method. 
And even C++, at the times when I programmed on this language, didn't have any
such "join" function in the standard library (that's why I needed to invent it; 
BTW, it's interesting, does C++ have join today... quick googling makes 
impression it doesn't...).

I never saw more simple implementation. All the implementations I saw have 
an IF inside the loop:
    result = '';
    for (i = 0; i < collection.length; i++) {
      if (i > 0) {
         result += ',';
      result += collection[i];

Here we also needed to change the "for each" construct to a "for by variable i", 
to determine if we are at the first iteration or not.

The maybeComma solution does not depend on the iteration construct at all.
Therefore it is easier to adopt. For example joining two collections.
In the maybeComma approach we just copy/paste the loop without rethinking
the logic, it always works the same:

    result = '';
    maybeComma = '';
    for (val : collectionA) {
      result += maybeComma + val;
      maybeComma = ',';
    for (val : collectionB) {
      result += maybeComma + val;
      maybeComma = ',';

In the IF approach we again need to change the control constructs by 
introducing a boolean flag:

    result = '';
    oneDone = false;
    for (i = 0; i < collectionA.length; i++) {
      if (oneDone) {
         result += ',';
      result += collectionA[i];
      oneDone = true;
    for (i = 0; i < collectionB.length; i++) {
      if (oneDone) {
         result += ',';
      result += collectionB[i];
      oneDone = true;

As far as I remember this understanding of how to avoid logic by switching to 
different data values came to me from ThinkingForth - the idea of how to use
the maybeComma variable to avoid the IF in the solution was a result of conscious 
attempt to apply what is taught in this book. 

Tuesday, 27 September 2011

Private Members in Javascript

It is usually advised to use closures to emulate private class members in javascript. Like this:

function MyClass(param) {

   var privateVar = 1;

   function privateMethod(a) {
      return a + 2;

   this.publicVar = param;

   this.publicMethod = function(x) {
     return x + this.publicVar - privateVar;

var my = new MyClass();

I find this approach inconvenient for various reasons. Right away I can remember at least 4:

  • Private members are not visible in debugger (FireBug)
  • You can't access private members programmatically (during development I often need to experiment and access private members)
  • Public methods which need access to private data should be defined inside of the closure, we can't add them to the class prototype.
  • In the code private and public members are referred differently: without this for private and with this for public. If I want to make some previously private method public, I need to find all the references and change method() to this.method(). And often the correct this is not available, because we are in some deeper function/closure, where this refers different object (for example we are in a callback of AJAX call). So, making a method public can not always be done mechanically.

We may try to workaround the last issue by defining even public methods as closure-local functions and then publishing them explicitly:

function MyClass(param) {

   // ...

   function publicMethod(x) {
     return x + publicVar - privateVar;

   function publicMethod2(x, y) {
     return publicMethod(x) + y;

  this.publicMethod = publicMethod;
  this.publicMethod2 = publicMethod2;

But that way descendant classes can't override the public methods. In short, the closure approach is unsatisfying.

I have an idea which looks for me better than using closures to emulate private members.

The class implementation may have everything accessible via this (i.e. public):

function MyClass(param) {
   this.privateVar = param;

MyClass.prototype.privateMethod = function (a) {
      return a + 2;

MyClass.prototype.publicMethod = function(x) {
     return x - this.privateVar;

var my = new MyClass();
Then, if I want to restrict class clients from using the private implementation details (and feel that code comments are not enough for that) I can provide them only public interface:
var my = {
   impl: new MyClass(1),
   publicMethod: function (x) {return this.imp.publicMethod(x);}

// This is how clients normally use it:

// But it is also possible to check the private implementation for debugging and development purposes:

This separation of encapsulation from classes resembles Common Lisp.

And the difference between obj.method() and obj.impl.method() is similar to Common Lisp: package-name:func-name for exported symbols and package-name::func-name for not-exported symbols. Client can access private implementation if he wants, but the syntax difference ensures he knows what he is doing.

Depending on the situation, we may create such interface objects manually, or create a little function which wraps any object into an interface:

// Whole the code below is tested, you can run it e.g. in FireBug console.

function as(impl, interface) {
  var wrapper = {impl: impl}; 
  for (prop in interface) {
    wrapper[prop] = makeImplCaller(prop);
  return wrapper;

function makeImplCaller(functionName) {
    return function() {
        return this.impl[functionName].apply(this.impl, arguments);

// This is the public interface
var Interface = {
  funcA: null, // (a)
  funcB: null, // (a, b)

// The implementation of the interface
function Impl (x, y) {
    this.privX = x;
    this.privY = y;

Impl.prototype.funcA = function (a) {
    return this.privX + a;

Impl.prototype.funcB = function (a, b) {
    return this.privX - a + this.privY - b;

// How to provide the inteface to client
var impl = new Impl(5, 10);
var i = as(impl, Interface);

// And client uses it:
// inspecting private details

Beware, in some cases the fact that implementation is wrapped into a wrapper might affect the program. If we compare o1 === o2, it might happen that o1 is impl, and 02 is a wrapper, and we get false when true is expected. This is the kind of problem many Java programmers face sometimes (in Java ecosystem various decorators, AOP, proxies, etc. are often used).

I had no time to think further about this approach. For example, in the above code making i instanceof Interface returning true.

But in general I like the idea.

Thursday, 2 June 2011

Unlucky day for OpenID

Tried today some small changes in cl-openid and faced various problems (in 3rd parties! verified it by unchanged cl-openid and also by testing with other online services).

Blogger OpenID provider is just broken (

Livejournal OpenID provider started to violate the spec (as I read it) by returning error response with HTTP status code 200 OK. (See Error Responses in the end of this section: and 8.2.4. Unsuccessful Response Parameters in the end of this section: When I tested Livejournal with cl-openid last time, it worked OK.

Update: Livejournal problem is solved by workaround in cl-openid. Blogger remains broken (including work with any other OpenID relying parties, not only cl-openid).

Monday, 24 January 2011

В C++ вводят лямбда функции и замыкания.

Я практически не пользуюсь С++ уже несколько лет, и не мог предположить, что благодать нисходит на него так интенсивно. Изменения языка довольно существенные. Сборщик мусора правда не вводят. Конечно, существующие коллекторы [2] никто не отменял, но в стандарт не включают.
1 -
2 -

Wednesday, 2 June 2010

Указ 60 .net

Актуально для белорусов:
Статистика за время с первой подписи 28 мая 10:49 по текущий момент 2 часа ночи 3 апреля. Число голосов в опросе 3301. Число "подписей" к письму в виде комментариев - 1806.
Возможно есть и другие подобные сайты, этот я нашел поиском в гугле. Нужно будет подумать, что еще можно сделать против этого вредного указа.

Monday, 3 May 2010

Прочитал на тут бае:

Апостол Павел считается небесным покровителем органов финансовых расследований Республики Беларусь.

Friday, 16 October 2009

Saturday, 22 August 2009

Extending IntelliJ IDEA using Common Lisp

If you have nothing to do, you can embed ABCL into IntelliJ IDEA and extend and control IDEA interactively from SLIME.

I created a little plugin that starts ABCL inside of IDEA:

Note: both videos for better resolution are opened in new window at their Vimeo URLs

How it works: "ABCL Start" command creates ABCL compiler and attaches it's REPL to a special "ABCL Console" tool window. Also it executes start.lisp file, which is supposed to be edited by user. User may do whatever he needs: start SLIME server or load other lisp files.

SLIME fuzzy-completion works for Java classes/methods because I use jfli (I found an ABCL port by Andras Simon). I wish fuzzy-completion work faster with ABCL (the latency may be up to half a second). But I believe ABCL will be improved in this area in the near future.

Hello Word -like examples in the video above are contained in the fun.lisp file from the plugin distribution.

Another example I prepared is implementation of Emacs-like "yanking" in IDEA (Ctrl-Y pastes an item from clipboard, and Alt-Y replaces just inserted item by next clipboard item). The code is contained in the yanking.lisp. Here it is in action:

To summarize, it is possible, and with some practice quite convenient, to use ABCL for IDEA extension.

If you have in mind some useful extension that may be implemented in Lisp, you can use my code as a boilerplate.

The sources:
Binary distro:

Some details are provided in the README file.

Saturday, 13 December 2008


Читал сейчас в википедии статью про Винни-Пуха. В частности, там рассказывается про звучание имени на разных языках. Например, в английском языке «h» в имени Pooh не произносится, это имя рифмуется постоянно с who или do.

В белорусском переводе некоего Виталя Воронова — Віня-Пых. Вторая часть имени переведена как «Пых», якобы потому, что это созвучно с белорусскими словами пыха (высокомерие и гордость) и запыхацца.

Sunday, 7 December 2008

Как скрыть рекламу в Live Journal

Я читаю несклько блогов в Live Journal и мне мешает громадный рекламный баннер на пол экрана, который Live Journal вставляет в блоги бесплатных аккаунтов.

AdBlock плагин к Firefox в данном случае не помог, он скрывает картинку, но тогда остается большое белое пространство, что не намного лучше картнинки.

В Google быстро нашлось решение.

Для Firefox

Создаем файл userContent.css:

/* Removes LJ Ads from pages. */
/* Code From ambience8 */

@-moz-document domain("") {

div.adv {display: none !important; visibility: hidden !important;}
div.ljad {display: none !important; visibility: hidden !important;}
div.ljadleaderboard-bottom {display: none !important; visibility: hidden !important;}
div.ljadleaderboard {display: none !important; visibility: hidden !important;}
div.ljadwrapper-journal-after-post-c {display: none !important; visibility: hidden !important;}
div.ljadwrapper-app-home {display: none !important; visibility: hidden !important;}
div.ljadmedrect {display: none !important; visibility: hidden !important;}

Этот файл нужно положить в директорию chrome вашего профиля. На Widows XP это будет C:\Documents and Settings\<Имя Пользователя>\Application Data\Mozilla\Firefox\Profiles\xxxxxxxx.default\chrome\userContent.css. В Windows Vista: C:\Users\<Имя Пользователя>\AppData\Roaming\Mozilla\Firefox\Profiles\xxxxxxxx.default\chrome\userContent.css

Теперь перезапускаем Firefox. В результате реклама в блогах Live Journal должна исчезнуть.

Для Internet Explorer

Создаем файл userContent.css:

/* Removes LJ Ads from pages. */
/* Code From ambience8 */

div.adv {display: none !important; visibility: hidden !important;}
div.ljad {display: none !important; visibility: hidden !important;}
div.ljadleaderboard-bottom {display: none !important; visibility: hidden !important;}
div.ljadleaderboard {display: none !important; visibility: hidden !important;}
div.ljadwrapper-journal-after-post-c {display: none !important; visibility: hidden !important;}
div.ljadwrapper-app-home {display: none !important; visibility: hidden !important;}
div.ljadmedrect {display: none !important; visibility: hidden !important;}

В меню Internet Explorer выбираем Tools -> Internet Options -> General -> Accessibility -> User style sheet. Здесь указываем путь к созданному файлу.

Как можно заметить из кода, решение для Internet Explorer будет скрывать элемены div с классом adv на любых страницах, а не только в, так что... это не страшно :)


Friday, 5 December 2008

Hunchentoot on ECL

It was reported that all Hunchentoot dependencies are already ported to ECL and a patch exists for Hunchentoot itself to make it running on ECL.

I am glad to hear this, because ECL is the only free Common Lisp on Windows with multithreading support (at the same time an active work on multithreading for CLISP is in progress, Clozure CL has beta port for Windows, and a lot of work was also done for threading in SBCL Windows port, although the work is not finished).

I tried Hunchentoot on ECL and for those who are interested too, I reproduce here the steps I took. My environment: Windows XP + Microsoft Visual Studio.

  1. You need the latest versions of Hunchentoot and all the dependencies. If you are lazy, you may get all them at once from this archive I prepared.
  2. Apply the patch from this post (already done in my archive): save the port-ecl.lisp to the hunchentoot directory and add #+:ecl (:file "port-ecl") to hunchentoot.asd as here.
  3. Checkout fresh ECL from CVS:
      cvs -z3 checkout ecl
    I recommend fresh checkout because I had problems building ECL from sources already existing on my computer after update with cvs update -dAP.
  4. Configure ECL. Open ecl\msvc\Makefile and set
    ECL_THREADS  = 1
    ECL_UNICODE  = 1
    ECL_ASDF     = 1
    ECL_SOCKETS  = 1
  5. Build ECL. Run Visual Studio command prompt: menu Start->All Programs -> Microsoft Visual Studio 2005 -> Visual Studio Tools -> Visual Studio 2005 Command Prompt. This script sets up environment variables so that C compiler cl.exe, nmake.exe and other things are in your PATH, all includes and libraries are available, etc. In this console window type:
     > cd <DIR_WITH_ECL_SOURCES>\ecl\msvc
     > nmake
    After the build completes, copy resulting binaries somewhere:
      > nmake install prefix=c:\somewhere\ecl
    Now you have working ECL, lets test it:
    > c:\somewhere\ecl\ecl.exe
    ECL (Embeddable Common-Lisp) 0.9l (CVS 2008-07-12 18:54)
    Copyright (C) 1984 Taiichi Yuasa and Masami Hagiya
    Copyright (C) 1993 Giuseppe Attardi
    Copyright (C) 2000 Juan J. Garcia-Ripoll
    ECL is free software, and you are welcome to redistribute it
    under certain conditions; see file 'Copyright' for details.
    Type :h for Help.  Top level.
    > (+ 2 2)
    > (quit)

    Do not close the Visual Studio command prompt, we will need it soon.

  6. As Hunchentoot uses ASDF, we need Hunchentoot with all its dependencies to be registered in ASDF:*CENTRAL-REGISTY*. For this I have a special lisp file, setup-asdf-registry.lisp (already included in the archive from the step 1):
    ;; this file must be stored in the same directory
    ;; where all the lisp libraries are stored 
    ;; (but libraries, of course, have their own subdirectories)
    (require 'asdf)
        ((reg (relative-lib-dir)
           (let ((lib-dir
                   (merge-pathnames relative-lib-dir
                                     (or #.*compile-file-pathname* 
             (pushnew lib-dir
                      :test #'equalp))))
      (reg "alexandria\\")
      (reg "asdf-binary-locations\\")
      (reg "babel_0.2.0\\")
      (reg "cffi-080926\\")
      (reg "chunga-0.4.3\\")
      (reg "cl-base64-3.3.2\\")
      (reg "cl-fad-0.6.2\\") 
      (reg "cl-ppcre-2.0.1\\")
      (reg "cl-who-0.11.0\\")
      (reg "cl+ssl\\")
      (reg "flexi-streams-1.0.7\\")
      (reg "hunchentoot-0.15.7\\")
      (reg "md5-1.8.5\\")
      (reg "rfc2388\\")
      (reg "trivial-features_0.1\\")
      (reg "trivial-gray-streams\\")
      (reg "url-rewrite-0.1.1\\"))
    (asdf:operate 'asdf:load-op :asdf-binary-locations) 
  7. Compile and load Hunchentoot. For this go back to the Visual Studio command prompt.
    > c:\somewhere\ecl\ecl.exe  
    > (load "c:/archive-from-the-step-1/setup-asdf-registry.lisp") 
    > (pushnew :hunchentoot-no-ssl *features*)
    > (asdf:operate 'asdf:load-op :hunchentoot-test)
    ECL must be started from Visual Studio command prompt because it uses C compiler when compiling Lisp. (pushnew :hunchentoot-no-ssl *features*) is only necessary if you do not have OpenSSL installed (get it here for Win32).
  8. Start Hunchentoot:
    > (hunchentoot:start-server :port 4242) 

Now you may open http://localhost:4242/hunchentoot/test and see Hunchentoot test site.

At the moment of this post writing, almost all examples from the test site work, except for two: "UTF-8 demo (writing UTF-8 characters directly to the stream)" and "UTF-8 demo (returning a string)". The reason is already found and I am sure it will be fixed very soon.

Blog Archive