One line paraphrase Lisp is nice and we used it and here's all the stuff that was broken with SBCL I had to fix.
We use SBCL for production deployment and CCL on most of the developers' machines. One of the nice things about Lisp is that you have an option of choosing from several mature implementations with different strengths and weaknesses: in our case, we optimized for processing speed on the server, and compilation speed in the dev environment
Quicklisp repository hosts more than 1,000 Lisp libraries — not a mind-blowing number but quite enough for covering all of our production needs.
Overall, the problems we face with integrating a Lisp app into the cloud world are not radically different from the ones we encounter with many other technologies. If you want to use Lisp in production, and to experience the joy of writing Lisp code, there is no valid technical reason not to!
oh noes!
We've built an esoteric application (even by Lisp standards), and in the process have hit some limits of our platform. One unexpected thing was heap exhaustion during compilation. We rely heavily on macros, and some of the largest ones expand into thousands of lines of low-level code. It turned out that SBCL compiler implements a lot of optimizations that allow us to enjoy quite fast generated code, but some of which require exponential time and memory resources.
We had to tune the generation sizes, and it turned out that the best option was to use an over-sized heap: our application consumes 2-4 gigabytes of memory, but we run it with 25G heap size which automatically results in a huge volume for the nursery. Yet another customization we had to make — a much less obvious one — was to run full GC programmatically every N minutes. With a large heap, we have noticed a gradual memory usage buildup over periods of tens of minutes which resulted in spans of more time spent in GC and decreased application throughput. Our periodic GC approach got the system into a much more stable state with almost constant memory usage.
oh noes!
Of all these challanges, the worst bug I've ever seen was a network bug. As usual with such stories, it was not a bug in the application but a problem in the underlying platform (this time — SBCL).
oh noes!
As we were just beginning running our service under substantial load in production, after some period of normal operation all of the servers would suddenly start to slow down and then would become unresponsive. After much investigation centering on our input data, we discovered that the problem was instead a race condition in low-level SBCL network code, specifically in the way the socket function getprotobyname, which is non-reentrant, was called.
This is useful
With trace, you define a function to trace, run the code, and Lisp prints all calls to that functions with arguments and all of its returns with results. It is somewhat similar to stacktrace, but you don't get to see the whole stack and you dynamically get a stream of traces, not stopping the application. trace is like print on steroids that allows you to quickly get into the inner workings of arbitrary complex code and monitor complicated flows. The only shortcoming is that you can't trace macros.
Name:
Anonymous2015-07-02 19:19
I've been doing LISP all wrong.
Name:
Anonymous2015-07-02 20:24
>>41 At the end of the day, it's just wise opinions of a Japanese EXPERT LISP PROGRAMMER who works at google. Unless you're writing a server application most of these are just good style.
<!-- <STYLEPOINT title="Do not abuse lists"> --> <STYLEPOINT title="リストの乱用はしない"> <SUMMARY> <!-- You must select proper data representation. You must not abuse the LIST data structure. --> データ表現は正しく選択しなければなりません。 <code>LIST</code>構造を乱用してはなりません。 </SUMMARY> <BODY> <p> <!-- Even though back in 1958, LISP was short for "LISt Processing", --> <!-- its successor Common Lisp has been a modern programming language --> <!-- with modern data structures since the 1980s. --> 1958の昔にまで遡ればLISPは"LISt Processing"の略でありましたが、1980年代以降、その後継であるCommon Lispはモダンなデータ構造を持ったモダンなプログラミング言語であり続け ています 。 <!-- You must use the proper data structures in your programs. --> プログラムには適切なデータ構造を使わなければなりません。 </p> <p> <!-- You must not abuse the builtin (single-linked) <code>LIST</code> --> <!-- data structure where it is not appropriate, --> <!-- even though Common Lisp makes it especially easy to use it. --> Common Lispは確かにビルトインの(単方向連結)<code>LIST</code>の操作が簡単になっていますが、不適切な場面でそれを乱用してはなりません。 </p> <p> <!-- You must only use lists --> <!-- when their performance characteristics --> <!-- is appropriate for the algorithm at hand: --> <!-- sequential iteration over the entire contents of the list. --> リストを使うときはそのパフォーマンスの特徴上適切なアルゴリズム ─ シーケンシャルにリスト全体を舐めるようなもの ─ で操作する場合に限らなければなりません。 </p> <p> <!-- An exception where it is appropriate to use lists --> <!-- is when it is known in advance --> <!-- that the size of the list will remain very short --> <!-- (say, less than 16 elements). --> 例外として事前にリストのサイズが小さい(具体的には要素数が16未満)と分かっている場合があげられます。 </p> <p> <!-- List data structures are often (but not always) --> <!-- appropriate for macros and functions used by macros at compile-time: --> <!-- indeed, not only is source code passed as lists in Common Lisp, --> <!-- but the macro-expansion and compilation processes --> <!-- will typically walk over the entire source code, sequentially, once. --> リストは大抵の場合(だがしかしいつもではない)マクロやコンパイル時に使われる関数には適切です。 というのも、Common Lispのソースコードがリストとして渡されるだけでなく、マクロ展開やコンパイル処理で典型的には全ソースコードを1回舐めるからです。 <!-- (Note that advanced macro systems don't directly use lists, but instead --> <!-- use abstract syntax objects that track source code location and scope; --> <!-- however there is no such advanced macro system --> <!-- in Common Lisp at this time.) --> (進んだマクロシステムでは直接はリストを使わずソースコードの位置やスコープを追跡する抽象構文オブジェクトを使いますが、現時点ではCommon Lispにはそのようなマクロシステム は存在しないことに気を付けましょう。) </p> <p> <!-- Another exception where it is appropriate to use lists is --> <!-- for introducing literal constants --> <!-- that will be transformed into more appropriate data structures --> <!-- at compile-time or load-time. --> もう一つの例外としてコンパイル時やロード時に適切なデータ構造に変換される[リストの]リテラルを導入する場合があげられます。 <!-- It is a good to have a function with a relatively short name --> <!-- to build your program's data structures from such literals. --> 比較的短かい名前でそのようなリテラルから自分のプログラムのデータ構造を構成する関数を用意するのは良いことです。 </p> <p> <!-- In the many cases when lists are not the appropriate data structure, --> <!-- various libraries such as --> <!-- <a href="http://cliki.net/cl-containers">cl-containers</a> or --> <!-- <a href="http://cliki.net/lisp-interface-library">lisp-interface-library</a> --> <!-- provide plenty of different data structures --> <!-- that should fulfill all the basic needs of your programs. --> リストがデータ構造として適切でない場合は<a href="http://cliki.net/cl-containers">cl-containers</a>や<a href="http://cliki.net/lisp-interface-library">lisp-interface-library</a>などのライブラリがプログラムの基本的な需要を全て満足する多種多様なデータ構造を提供してくれます。 <!-- If the existing libraries are not satisfactory, see above about --> <!-- <a href="#Using_Libraries">Using Libraries</a> and --> <!-- <a href="#Open-Sourcing_Code">Open-Sourcing Code</a>. --> もし既存のライブラリで満足できないなら、<a href="#Using_Libraries">Using Libraries</a>や<a href="#Open-Sourcing_Code">Open-Sourcing Code</a>を参考にして下さい。 </p> </BODY> </STYLEPOINT>
Wow I never knew about this. Common Lisp is an acceptable D.
<STYLEPOINT title="DYNAMIC-EXTENT"> <SUMMARY> <!-- You should only use <code>DYNAMIC-EXTENT</code> --> <!-- where it matters for performance, --> <!-- and you can document why it is correct. --> <code>DYNAMIC-EXTENT</code> の利用はパフォーマンスが重要である場合に限るべきです。 さらには、宣言の利用の正当性を文書化しておきます。 </SUMMARY> <BODY> <p> <!-- <code>DYNAMIC-EXTENT</code> declarations are --> <!-- a particular case of --> <!-- <a href="#Unsafe_Operations">unsafe operations</a>. --> <code>DYNAMIC-EXTENT</code>宣言は<a href="#Unsafe_Operations">安全でない操作</a>のケースの1つです。 </p> <p> <!-- The purpose of a <code>DYNAMIC-EXTENT</code> declaration --> <!-- is to improve performance by reducing garbage collection --> <!-- in cases where it appears to be obvious that an object's lifetime --> <!-- is within the "dynamic extent" of a function. --> <!-- That means the object is created at some point --> <!-- after the function is called, and --> <!-- the object is always inaccessible after the function exits by any means. --> <code>DYNAMIC-EXTENT</code> 宣言の目的は、 該当オブジェクトの生存期間が関数の“動的エクステント″の範囲に収まっていることが明白な場合において、 ガベージ・コレクションの発生を抑制することにより、パフォーマンスを向上させることにあります。 この動作が意味するところは、関数が呼ばれることにより、ある時点で該当オブジェクトが生成されるとして、 この関数の実行が終了した後には、該当オブジェクトには、どのような意味でも常にアクセス不可能になる、ということです。 </p> <p> <!-- By declaring a variable or a local function <code>DYNAMIC-EXTENT</code>, the programmer <em>asserts</em> to Lisp that any object that is ever a value of that variable or the closure that is the definition of the function has a lifetime within the dynamic extent of the (innermost) function that declares the variable. もってまわりすぎ? --> 変数や局所関数に<code>DYNAMIC-EXTENT</code>宣言を付けることにより、プログラマは、 変数やクロージャーの寿命は(最内周の)関数の動的エクステント内であることを、Lisp処理系に<em>強引に指定</em>することになります。 </p> <p> <!-- The Lisp implementation is then free to use that information --> <!-- to make the program faster. --> 宣言時、Lisp処理系は、任意でこの情報をプログラムの速度向上のために活用することができます。 <!-- Typically, Lisp implementations can take advantage of this knowledge --> <!-- to stack-allocate: --> 典型的には、Lisp処理系は、この知識をスタック割付けに役立てます: </p> <ul> <li> <!-- The lists created to store <code>&REST</code> parameters. --> <code>&REST</code> 引数により作られるリスト </li> <li> <!-- Lists and vector allocated within a function. --> <!-- Lists, vectors and structures allocated within a function. --> アロケートが関数内で収まっているリスト、ベクタ、構造体 </li> <li> <!-- Closures. --> クロージャー </li> </ul> <p> <!-- If the assertion is wrong, i.e. if the programmer's claim is not true, --> <!-- the results can be <em>catastrophic</em>: --> <!-- Lisp can terminate any time after the function returns, --> <!-- or it can hang forever, or — worst of all — --> <!-- it can produce incorrect results without any runtime error! --> もし指定が間違っていれば、つまりプログラマの主張が真でなければ、<em>大災害</em>にもなるでしょう: Lispは[dynamic-extent指定をした]関数の値が返るときいつでも終了、あるいはフリーズ、最悪の場合エラーすら起さずに間違った値を返し得ます。 </p> <p> <!-- Even if the assertion is correct, --> <!-- future changes to the function might introduce --> <!-- a violation of the assertion. --> もし指定が正しかったとしても将来関数を変更するときに暴発しかねません。 <!-- This increases the danger. --> 危険性は増大します。 </p> <p> <!-- In most cases, such objects are ephemeral. --> たいていの場合、そのような[dynamic-extent宣言を付けたくなるような]オブジェクトは短命です。 <!-- Modern Lisp implementations use generational garbage collectors, --> <!-- which are quite efficient under these circumstances. --> モダンなLisp処理系は世代別ガーベジコレクションを使いますが、それはこのような状況においては非常に効率的です。 </p> <p> <!-- Therefore, <code>DYNAMIC-EXTENT</code> declarations --> <!-- should be used sparingly. You must only use them if: --> それゆえ、<code>DYNAMIC-EXTENT</code>宣言は滅多に使うべきではありません。使うべき状況は: </p> <ol> <li> <!-- There is some good reason to think that the overall effect --> <!-- on performance is noticeable, and --> パフォーマンスにおける総合的な影響が顕著であると考えるに十分な理由がある。 </li> <li> <!-- It is absolutely clear that the assertion is true. --> その指定が自明に正しい。 </li> <li> <!-- It is quite unlikely that the code will be changed --> <!-- in ways that cause the declaration to become false. --> その宣言が正しくなくなるような変更が加えられる可能性が非常に低い。 </li> </ol> <p> <!-- Point (1) is a special case of --> <!-- the principle of avoiding premature optimization. --> 1つめの項目は早過ぎる最適化を避けるの原則の特別な場合です。 <!-- An optimization like this only matters if such objects --> <!-- are allocated at a very high rate, e.g. "inside an inner loop". --> このような最適化は「[ネストした]内側のループの中」などの非常に高い頻度でアロケートされるような場合にのみ意味があります。 </p> <p> <!-- Note that is relatively easy to ascertain that --> <!-- a function will not escape the dynamic extent of the current call frame --> <!-- by analyzing where the function is called and --> <!-- what other functions it is passed to; --> <!-- therefore, you should somewhat wary of declaring a function --> <!-- <code>DYNAMIC-EXTENT</code>, but this is not a high-stress declaration. --> 関数がどこで呼ばれてどの別の関数に渡されるかを分析すれば、その関数が現在のコールフレームの動的スコープを抜けないことを突き止めることは比較的容易なので、関数に<code>DYNAMIC-EXTENT</code>を宣言するときは用心する必要はありますが、これはそれほどストレスの高いものではないことを覚えておきましょう。 <!-- On the other hand, it is much harder to ascertain that --> <!-- none of the objects ever bound or assigned to that variable --> <!-- and none of their sub-objects --> <!-- will escape the dynamic extent of the current call frame, --> <!-- nor will in any future modification of a function. --> <!-- and that they still won't in any future modification of a function. --> 逆に、ある変数に束縛されたオブジェクトとそのサブオブジェクトのどれもが現在のコールフレームの動的エクステントを抜けない、そして将来の修正のときにも影響も受けないことを 確実にするのは非常に困難を極めます。 <!-- Therefore, you should be extremely wary --> <!-- of declaring a variable <code>DYNAMIC-EXTENT</code>. --> それゆえ、変数に<code>DYNAMIC-EXTENT</code>を宣言するときは細心の注意を払いましょう。 </p> <p> <!-- It's usually hard to predict the effect of such optimization on performance. --> 通常、このようなパフォーマンスの最適化の影響を予測するのは難しいものです。 <!-- When writing a function or macro --> <!-- that is part of a library of reusable code, --> <!-- there's no a priori way to know how often the code will run. --> 再利用可能なライブラリの一部として関数やマクロを定義するときはどのくらい使われるかを知る有効な手段はありません。 <!-- Ideally, tools would be available to discover --> <!-- the availability and suitability of using such an optimization --> <!-- based on running simulations and test cases, but --> <!-- in practice this isn't as easy as it ought to be. --> 理想的には、シュミレーションをしてそのような最適化が使えるか、適当であるかを判断するツールが使えると良いのですが、実際には難しいものがあります。 <!-- in practice this isn't as easy as it ought to beを意訳しました --> <!-- It's a tradeoff. --> トレードオフです。 <!-- If you're very, very sure that the assertion is true --> <!-- (that any object bound to the variable and any of its sub-objects --> <!-- are only used within the dynamic extent of the specified scope), --> <!-- and it's not obvious how much time will be saved --> <!-- and it's not easy to measure, --> <!-- then it may be better to put in the declaration than to leave it out. --> 変数に束縛されたオブジェクトとそのサブオブジェクトのどれもが動的[エクステントとなるような]スコープでしか使われないという絶対の自身があって、どの程度最適化の効果がある か分からず、容易に効果の測定もできないなら、そのまま放っておくよりは、宣言する方が良いでしょう。 <!-- (Ideally it would be easier to make such measurements --> <!-- than it actually is.) --> (もっと測定が容易であれば良いのですが。) </p> </BODY> </STYLEPOINT>
<STYLEPOINT title="REDUCE か APPLY か"> <SUMMARY> <!-- You should use <code>REDUCE</code> --> <!-- instead of <code>APPLY</code> where appropriate. --> <code>APPLY</code> を利用する方がより適切な場合を除いて <code>REDUCE</code> を利用すべきです。 </SUMMARY> <BODY> <p> <!-- You should use <code>REDUCE</code> --> <!-- instead of <code>APPLY</code> and a consed-up list, --> <!-- where the semantics of the first operator argument --> <!-- otherwise guarantees the same semantics. --> <!-- Of course, you must use <code>APPLY</code> --> <!-- if it does what you want and <code>REDUCE</code> doesn't. --> <!-- For instance --> 第一引数の関数の意味論が同じと保証されるなら、<!-- ? --> リストを作って<code>APPLY</code>する代りに<code>REDUCE</code>を使うべきです。 勿論、<code>REDUCE</code>では駄目で、求めるものが<code>APPLY</code>であるなら使わなければなりません。 例えば: </p> <BAD_CODE_SNIPPET> ;; Bad (apply #'+ (mapcar #'acc frobs)) </BAD_CODE_SNIPPET> <CODE_SNIPPET> ;; Better (reduce #'+ frobs :key #'acc :initial-value 0) </CODE_SNIPPET> <p> <!-- This is preferable because it does not do extra consing, --> <!-- and does not risk going beyond <code>CALL-ARGUMENTS-LIMIT</code> --> <!-- on implementations where that limit is small, --> <!-- which could blow away the stack on long lists --> <!-- (we want to avoid gratuitous non-portability in our code). --> これは、余計なコンスをしないのと、<code>CALL-ARGUMENTS-LIMIT</code> の上限が小さい処理系において、長いリストでスタックをあふれさせるリスクがない点で好ましいと言えます。 (コードに無用な可搬性の無さを持ち込むのは避けたい) </p> <p> <!-- However, you must be careful not to use <code>REDUCE</code> --> <!-- in ways that needlessly increase --> <!-- the complexity class of the computation. --> <!-- For instance, <code>(REDUCE 'STRCAT ...)</code> is <i>O(n^2)</i> --> <!-- when an appropriate implementation is only <i>O(n)</i>. --> <!-- Moreover, <code>(REDUCE 'APPEND ...)</code> --> <!-- is also <i>O(n^2)</i> unless you specify <code>:FROM-END T</code>. -->
<!-- In such cases, you must use proper abstractions --> <!-- that cover those cases instead of calling <code>REDUCE</code>, --> <!-- first defining them in a suitable library if need be. -->
<!-- In such cases, you MUST NOT use <code>REDUCE</code>, --> <!-- and you MUST NOT use <code>(APPLY 'STRCAT ...)</code> --> <!-- or <code>(APPLY 'APPEND ...)</code> either. --> <!-- Instead you MUST use proper abstractions --> <!-- from a suitable library (that you may have to contribute to) --> <!-- that properly handles those cases --> <!-- without burdening users with implementation details. --> <!-- See for instance <code>UIOP:REDUCE/STRCAT</code>. --> とはいうものの、不要に計算量を増大させるような<code>REDUCE</code>の用法には注意しなければなりません。 例えば、<code>(REDUCE #'STRCAT ...)</code>を用いれば<i>O(n^2)</i>となりますが、 [<code>REDUCE</code>ではなく]適切な実装を用いた場合には<i>O(n)</i>にしかなりません。 加えて、<code>(REDUCE #'APPEND ...)</code> もまた、<code>:FROM-END T</code>を指定しない限り<i>O(n^2)</i>となります。 このような場合、<code>REDUCE</code>を使ってはなりませんし、<code>(APPLY 'STRCAT ...)</code>や、<code>(APPLY 'APPEND ...)</code>も使ってはなりません。 代わりに、実装の詳細についてをユーザーが負担することなしに、このようなケースを上手く回せるような、 利用するにふさわしいライブラリ(もしかするとあなたが協力する必要もあるかもしれない)から適切な抽象化を提供するものを利用しなくてはなりません。 例としては、<code>UIOP:REDUCE/STRCAT</code>を参照のこと。 </p>
<!-- Note that if you start writing a new system --> <!-- in a heavily functional style, --> <!-- you may consider using --> <!-- <a href="http://cliki.net/lambda-reader">lambda-reader</a>, --> <!-- a system that lets you use the unicode character <code>λ</code> --> <!-- instead of <code>LAMBDA</code>. --> <!-- But you must not start using such a syntactic extension --> <!-- in an existing system without getting permission from other developers. --> 加えて、関数型スタイルを重用してシステムを新規に書き始める際は、 <a href="http://cliki.net/lambda-reader">lambda-reader</a>の使用を考えてみても良いでしょう。 これは、Unicodeの<code>λ</code>を<code>LAMBDA</code>の代わりに使えるライブラリです。 しかし、既にあるシステムに他の開発者の賛同なくこういった構文拡張を導入してはなりません。