スキームvs Common Lisp:どの特性がプロジェクトに違いをもたらしましたか?[閉まっている]


155

StackOverflowとこのサイトの両方で、あいまいな「Scheme vs Common Lisp」の質問が不足しているわけではないので、これにもっと焦点を当てたいと思います。質問は、両方の言語でコーディングした人向けです。

Schemeでコーディングしているときに、Common Lispコーディングエクスペリエンスで最も見逃した要素は何ですか?または、逆に、Common Lispでコーディングしているときに、Schemeでコーディングすることから何を逃しましたか?

言語機能だけを意味するわけではありません。以下は、質問に関する限り、見逃してはならない有効なものです。

  • 特定のライブラリ。
  • SLIME、DrRacketなどの開発環境の特定の機能
  • CコードのブロックをSchemeソースに直接書き込むGambitの機能など、特定の実装の機能。
  • そしてもちろん、言語機能。

私が望んでいる種類の答えの例:

  • 「Common LispでXを実装しようとしていたので、もしSchemeの第一級の継続があれば、Yをやっただけでしたが、代わりにZをやらなければならなかったので、苦労しました。」
  • 「Schemeプロジェクトでのビルドプロセスのスクリプト作成は、ソースツリーが成長し、Cライブラリをより多くリンクするにつれてますます苦しくなりました。次のプロジェクトでは、Common Lispに戻りました。」
  • 「既存の大きなC ++コードベースがあり、私にとっては、Gambit SchemeコードにC ++呼び出しを直接埋め込むことができたのは、SchemeがCommon Lispに対して持っていた短所、SWIGサポートの欠如を含めて、まったく価値がありました。」

ですから、「スキームはもっとシンプルな言語です」などの一般的な感情ではなく、戦争の物語を望んでいます。


25
優れた、わかりやすい質問。私はこのことに興味があります。うまくいけば、両方の言語の専門知識を持った人々が洞察を提供してくれることを願っています。
ロバートハーヴェイ

1
@ジョシュK-それは明らかに答えられますが、必ずしも単一の決定的な答えがあるわけではありません。誰もが誰もがおっしゃるような素晴らしさの答えを出してくれるので、私はそこにいるに違いないということを除いて!
グレナトロン

4
@Josh:それなら、おそらくSchemeとCommon Lispに慣れていないでしょう。両方の言語はそれ自体非常に強力ですが、どちらも主流として受け入れられていません。どうしてこれなの?方言がたくさんあるからかもしれません。どちらを選びますか?この種の比較は非常に明快である可能性があります。OPは質問を慎重に表現して、私の見解では非常に具体的かつ回答可能な回答に範囲を限定しました。
ロバートハーヴェイ

13
皆さん、あなたがそれを好まない、またはそれに関係できないという理由だけで、質問を閉じないでください。それは明らかに「本当の」質問です。終了するより適切な理由が見つからない場合は、終了するよう投票しないでください。
ロバートハーヴェイ

4
リチャード・ストールマンに彼の答えをメールすることができます。
wassimans

回答:


100

私の学部の学位は認知科学と人工知能でした。それから、Lispの1コースのイントロがありました。この言語は(「エレガント」のように)おもしろいと思っていましたが、Greenspunの第10規則に出くわすまではあまり考えませんでした。

十分に複雑なCまたはFortranプログラムには、Common Lispの半分の、アドホックで、非公式に指定され、バグに乗った、遅い実装が含まれています。

Greenspunのポイントは、(部分的に)多くの複雑なプログラムに組み込みのインタープリターがあることでした。インタプリタを言語に組み込むのではなく、すでにインタプリタ(またはコンパイラ)が組み込まれているLispのような言語を使用する方が意味があるかもしれないと提案しました。

当時私は、カスタム言語のカスタムインタープリターを使用してユーザー定義の計算を実行するかなり大きなアプリで作業していました。大規模な実験として、Lispでコアを書き直すことにしました。

およそ6週間かかりました。元のコードは、〜100,000行のDelphi(Pascalバリアント)です。Lispでは、これは〜10,000行に削減されました。しかし、さらに驚くべきことは、Lispエンジンが3〜6倍高速だったという事実です。そして、これはLisp初心者の仕事だということを覚えておいてください!その経験全体は私にとって非常に目を見張るものでした。パフォーマンスと表現力を単一の言語で組み合わせる可能性を初めて見ました。

しばらくして、Webベースのプロジェクトに取り組み始めたとき、いくつかの言語を試聴しました。LispとSchemeをミックスに含めました。最終的に、Schemeの実装(Chez Scheme)を選択しました。結果にとても満足しています。

Webベースのプロジェクトは、高性能の「選択エンジン」です。Schemeは、データの処理からデータのクエリ、ページ生成まで、さまざまな方法で使用します。多くの場所で、実際には別の言語で始めましたが、以下で簡単に説明する理由でSchemeに移行しました。

これで(少なくとも部分的に)あなたの質問に答えることができます。

オーディションでは、さまざまなLispおよびSchemeの実装を調べました。Lisp側では、Allegro CL、CMUCL、SBCL、およびLispWorksを(私は信じています)見ました。Scheme側では、Bigloo、Chicken、Chez、Gambitを(私は信じています)見ました。(言語の選択はかなり前のことでした。だから私は少しmんでいます。重要な場合はメモを掘り当てることができます。)

すぐに、a)ネイティブスレッドとb)Linux、Mac、およびWindowsのサポートを探していました。これらの2つの条件を組み合わせることで、全員がノックアウトされましたが、AllegroとChezは除外されました。したがって、評価を続けるためには、マルチスレッド要件を緩和する必要がありました。

一連の小さなプログラムをまとめて、評価とテストに使用しました。それは多くの問題を明らかにしました。たとえば、一部の実装には、一部のテストの実行が完了するのを妨げる欠陥がありました。一部の実装は実行時にコードをコンパイルできませんでした。一部の実装では、実行時にコンパイルされたコードとプリコンパイルされたコードを簡単に統合できませんでした。一部の実装には、他のものより明らかに優れた(または明らかに悪い)ガベージコレクタがありました。等

私たちのニーズでは、3つの商用実装(Allegro、Chez、Lispworks)のみが主要なテストに合格しました。3人のうち、Chezだけがすべてのテストに合格しました。当時、私はLispworksがどのプラットフォームにもネイティブスレッドを持っていなかったと思うし(現在はそうだと思う)、Allegroは一部のプラットフォームにのみネイティブスレッドを持っていると思う。さらに、Allegroには「電話してください」という実行時ライセンス料がありましたが、これはあまり好きではありませんでした。Lispworksにはランタイム料金がなく、Chezには簡単な(そして非常に合理的な)取り決めがあったと思います(そして、ランタイムにコンパイラーを使用した場合にのみ作動しました)。

LispとSchemeの両方でやや重要なコードの塊を生成したのは、比較と対照のポイントです。

  • Lisp環境ははるかに成熟しています。あなたは金のためにより多くの強打を得ます。(とはいえ、コードが増えるとバグも増えます。)

  • Lisp環境を学ぶのははるかに困難です。熟達するにはもっと時間が必要です。Common Lispは巨大な言語であり、商用実装がその上に追加するライブラリに到達する前です。(とはいえ、Schemeの構文ケースは、Lispのどの1つよりもはるかに微妙で複雑です。)

  • Lisp環境では、バイナリを生成するのが多少難しくなる可能性があります。不要なビットを削除するには、イメージを「振る」必要があります。 。対照的に、Chezでは、必要な他のすべてのファイルを含むトップレベルファイルをコンパイルし、完了です。

以前に、当初は意図していなかった多くの場所でSchemeを使用することになったと言いました。どうして?頭の上の3つの理由を考えることができます。

まず、Chez(およびその開発者であるCadence)を信頼することを学びました。ツールから多くのことを尋ねたところ、一貫して配信されました。たとえば、Chezの歴史的な欠陥はごくわずかですが、そのメモリマネージャは非常に優れています。

次に、Chezから得たパフォーマンスを愛することを学びました。スクリプト言語のように感じられるものを使用していましたが、それからネイティブコードの速度が得られました。重要ではないものもありますが、けがをすることはなく、時には非常に多くの助けになりました。

第三に、Schemeが提供できる抽象化を愛することを学びました。ところで、マクロを意味するだけではありません。私はクロージャー、ラムダ、テールコールなどのようなものを意味します。これらの用語で考え始めると、他の言語は比較によってかなり制限されているように見えます。

Schemeは完璧ですか?番号; それはトレードオフです。まず、個々の開発者がより効果的になることを可能にしますが、ほとんどの言語が持っている標識(forループなど)がSchemeにないため、開発者が互いのコードを理解するのがより困難になります(たとえば、百万通りの方法があります) forループ)。第二に、話をしたり、雇用したり、借りたりする開発者のプールがはるかに少ない。

まとめると、LispとSchemeは他のどこでも広く利用できない機能を提供していると思います。その機能はトレードオフであるため、特定のケースで意味のあるものであることが望ましいです。私たちの場合、LispとSchemeのどちらを使用するかを決定する要因は、言語やライブラリの機能よりも非常に基本的な機能(プラットフォームサポート、プラットフォームスレッド、ランタイムコンパイル、ランタイムライセンス)に関係していました。繰り返しますが、この場合もトレードオフでした。Chezを使用すると、必要なコア機能が得られましたが、商用Lisp環境にあった広範なライブラリが失われました。

また、繰り返しになりますが、私たちはずっと前にさまざまなLispとSchemeを見てきました。それ以来、すべてが進化し改善されてきました。


1
うわー、それはなんとかLisp実装よりも3-6倍遅く実行することができたなら、それは本当に恐ろしいDelphiコードだったに違いありません!:(
メイソンウィーラー

2
+1:この投稿で最も興味深いのは、Lispで大規模なプロジェクトを完了した後にLispからSchemeに切り替えたという事実です。(またはcomp.lang.lispにあまりにも長く潜んでいたのかもしれません。)
ラリーコールマン

25
「なんと、それがなんとかLisp実装よりも3-6倍遅く実行されるなら、それは本当に恐ろしいDelphiコードだったに違いありません!」そうです、私はそれをもっとうまく説明しなかったことに失敗したとみなします。Lisp実装は、ユーザー式をLisp式に変換することができました-これは非常に簡単なプロセスです-そして、Lisp式をネイティブコードにコンパイルします(完全に最適化されています)。それがグリーンスパンの第10規則の意味です。
マイケルレナハン

1
素晴らしい答え!少なくともより良いものが現れるまで、私はそれを選択します:) 1つの質問:あなたは、フィールドの状態に基づいて「ずっと前」にChez Schemeを使用する決定をしたと言います。年を指定できますか?
スーパーエレクトリック

11
その点、LISPの実装は、インタープリターに依存するのではなく、マシンコードに何かを自由にコンパイルできるため、微妙で非常に便利です。「Let Over Lambda」という本は、これがまさに、PERL正規表現構文を複製するポータブルCommon LISP正規表現パッケージが、PERLを大幅に上回る理由であると指摘しています。PERLには、正規表現インタープリターがあります。Common LISPパッケージは、正規表現をコードにコンパイルします。
ジョンR.ストローム

37

私は通常、回答としてリンクを貼り付けるのは好きではありませんが、このことについてブログ記事を書きました。網羅的ではありませんが、いくつかの主要なポイントがあります。

http://symbo1ics.com/blog/?p=729

編集:ここに主なポイントがあります:

  1. 存在:両方のlispsは他のlispsの束の後に来ました。Schemeは最小限の公理的ルートを取りました。CLはバロック様式のルートを取りました。
  2. CASE:通常、Schemeでは大文字と小文字が区別されます。CLはそうではありません(可能ですが)。これは時々見逃されますが、その実用性は議論されています(私によって)。
  3. 名前:CLのシンボルの名前は、しばしば奇妙でわかりにくいものです。TERPRIPROGNなど。スキームは通常、非常にわかりやすい名前を持っています。これはCLで見逃されたものです。
  4. 機能:CLには個別の機能名前空間があります。これはSchemeで見逃されません。通常、単一の名前空間を使用すると、非常にクリーンな関数型プログラミングが可能になりますが、これは多くの場合CLでは困難または厄介です。ただし、コストがかかります。Schemeで「list」から「lst」のような名前を難読化する必要がある場合があります。
  5. マクロ:Schemeで最も低レベルのダーティマクロが恋しいです。ええ、syntax-rulesあなたが本当にいくつかのことをハックしたいと思うまで、すべて元気です。一方、CLでは衛生的なマクロが見落とされることがあります。それらを行う標準的な方法がないということは、車輪を再発明することを意味します。
  6. 移植性:両方の言語が標準化されているにもかかわらず、CLの移植性が高い場合がよくあります。CLはより大きく、したがって、外部ライブラリなしで使用するより多くの標準機能があります。また、より実装に依存することを移植可能にできることも意味します。また、Schemeには1兆個の実装がありますが、そのほとんどは多少互換性がありません。これにより、CLが非常に望ましいものになります。
  7. 図書館:私の最後のポイントに非常に関連しています。スキームにはSRFIがありますが、広く認められているわけではありません。ライブラリを操作する移植可能な方法はありません。一方、CLには方法があります。また、Quicklispはgod(Xach)からの贈り物です。使用するライブラリの一種のリポジトリです。
  8. 実装:Schemeは非常に多くの実装を持つことに苦しんでいます。実際の標準的な実装はありません。一方、CLには、非常に優れた高性能または特定用途の実装がいくつかあります(高性能:SBCL、商用:Allegro、組み込み:ECL、ポータブル:CLISP、Java:ABCL、...)。

私は最初の人と少し上で話しただけですが、私が見逃していることと見逃していないことは明らかです。

[これらが一般的すぎる場合は謝罪します。もっと具体的な詳細が必要なようです。投稿にはいくつかの詳細があります。]


(本当に)短いティーザーの要約はどうですか?^^
デイブO.

2
ハイライトをインラインしてください。回答は独立している必要があります。

1
@Dave O.および@ThorbjørnRavn Andersen:要求に応じて要約を追加しました。ありがとう。
-Quadrescence

2
「バロックルート」!なんて素晴らしい方法でしょう。
マークC

Common Lispは大文字と小文字を区別しますが、評価する前に入力を大文字に変換します。シンボル内の小文字を引用符で囲むことで取得できます。名前の問題は、Schemeが古い古い名前を削除し、CLが削除しなかったためです。
デビッドソーンリー

25

私は最近、CバージョンとJavaバージョンを持つライブラリを使用してホームプロジェクトを開始しました。私はプロジェクトにLispを使用したかったので、Common Lisp、Scheme、またはClojureを使用する間、約1か月を費やしました。私は3つすべての経験がありますが、おもちゃのプロジェクトのみです。どちらを選んだかを話す前に、それぞれの経験について少しお話しします。

PLTラケットには、エディターから式を評価できるだけでなく、括弧の代わりに角括弧を入力して、必要に応じて括弧に戻すことができる素晴らしいIDEがあります。また、ラケットにはインストール済みのライブラリの大規模なセットがあり、さらにダウンロードできます。ビジュアルデバッガも役立ちます。

私のCommon Lisp実装(SBCL)にはIDEがありませんが、EmacsとSLIMEを使用するオープンソースCL実装では慣例です。この組み合わせは非常に効率的です。ソースファイルに入力した式を評価する機能に加えて、emacsのすべての編集コマンドを使用できるREPLがあるため、コードのコピーは両方の方法で効率的に実行できます。REPLバッファーに表示されているオブジェクトでも、コピーして貼り付けることができます。Alt+(そして、Alt+)一致する括弧とインデントを扱うための効率的です。

上記のEmacs機能はすべてClojureでも使用できます。Clojureでの私の編集経験は、Lispのそれに似ています。Javaの相互運用は問題なく機能しました。Clojureプロジェクトが完成したら、それをやりたいと思います。

3つすべて(Common Lisp、Racket、およびClojure)を使用してライブラリにアクセスできましたが、プロジェクトにCommon Lispを選択することになりました。決定要因は、FFIがCommon Lispではるかに使いやすいことでした。CFFIには、サンプルコードと各メソッドの詳細な説明が記載された非常に優れたマニュアルがあります。午後に20個のC関数をラップすることができたので、それ以降コードに触れる必要はありません。

もう1つの要因は、ClojureやR6RSスキームよりもCommon Lispに精通していることです。私はPractical Common LispとGrahamの本のほとんどを読んでおり、Hyperspecに満足しています。まだ「lispy」なコードではありませんが、より多くの経験を積むにつれて変更されると確信しています。


詳細をありがとう!SBCLのFFIはClojureのFFIよりも使いやすいと思っていたことを正しく理解できますか?そうだとすれば、ClojureからJavaメソッドをラップせずに直接呼び出すことができるので、それにはかなり驚かされます。(または、ネイティブコードを呼び出す必要がありましたか?)
SuperElectric

6
@SuperElectric:Clojureから「組み込み」Javaメソッドを呼び出すのは簡単です。ダウンロードしたライブラリにあるJavaメソッドの呼び出し:それほど多くはありません。クラスパスとインポート行を正しく取得するのに、CFFIを使用してSBCLから最初のCメソッドを取得するのにかかった時間よりも多くの時間を費やしました。しかし、私はJavaの専門家ではないため、マイレージは異なる場合があります。
ラリーコールマン

21

CLとラケットの両方でプログラムします。

現在、Common LispでWebサイトを開発しており、ラケットで働いていた以前の雇用主向けに社内プログラムスイートを作成しました。

社内コードについては、雇用主がWindowsショップであり、LispWorksの代金を支払うことができなかったため、Racket(当時はPLT Schemeと呼ばれていました)を選択しました。Windows向けの唯一の優れたオープンソースCL実装はCCLであり(現在もそうです)、CCLにはプロセッサでのSSEサポートが必要です。雇用主は安価で、Stone Ageハードウェアを使用していました。雇用主がまともなハードウェアを持っていたとしても、Common Lispでの結果の唯一のGUIライブラリはMcCLIMであり、これはUnixでのみ動作します。RacketにはUnixとWindowsの両方で動作する優れたGUIライブラリがあり、これは私のプロジェクトの成功にとって重要でした。

私は1年以上、原始的なDrRacketエディターに我慢しました。EMACSは、MrEdとして知られていたGUIバージョンのRacketをWindowsで劣等なものにすることはできませんでした。1回のキーストロークでカーソル位置の式を評価することができずにやらなければなりませんでした。代わりに、手動でS-expressionを選択してコピーし、REPLウィンドウをクリックして(切り替えるためのキーストロークがないため)、S-expressionを貼り付ける必要がありました。また、使用している関数またはマクロの予想される引数を表示できるエディターなしで実行する必要がありました。DrRacketは、SLIMEに代わるものではありません。

雇用主は複雑なXML APIを備えた独自のデータベースを使用していましたが、そのバージョンのSELECTクエリに応答するには、一見不必要な情報が大量に必要でした。このAPIにXMLを送信し、応答を解析するために、HTMLPragを使用することにしました。うまくいきました。

SQLのようなフォームを入力することで、複雑すぎるXML APIとやり取りできるマクロを作成するために、ラケットの複雑な「構文ケース」マクロシステムを学習する必要がありました。DEFMACROを自由に使えば、この部分はずっと簡単だったでしょう。ただし、達成するのにより多くの労力がかかったにもかかわらず、最終結果は依然としてシームレスでした。

さらに、Common LispのLOOPマクロなしでやらなければなりませんでした。Racketは、ほとんどのコードを記述した後にのみ代替手段の提供を開始しましたが、LOOPに比べて代替手段はいまだにひどいです(Racketの開発チームは、それが優れていると主張しているにもかかわらず、単に間違っているだけです)。最終的に、「car」と「cdr」を使用してリストを反復処理する名前の付いたLETフォームを多数作成しました。

carとcdrについて言えば、Schemeの(car '())がエラーであると解釈することほどイライラするものはありません。Racketの大文字と小文字の区別を利用して、Common Lispのセマンティクスを持つCARとCDRを実装しました。ただし、 '()と#fを分離することにより、'()をデフォルト値として返すことはあまり役に立ちません。

また、UNWIND-PROTECTを再実装し、Racketが残したギャップを埋めるために独自の再起動システムを発明しました。Racketコミュニティは、再起動が非常に便利で実装しやすいことを学ぶ必要があります。

ラケットのlet-valuesフォームは非常に冗長であるため、MULTIPLE-VALUE-BINDを実装しました。ラケットで、使用するかどうかにかかわらず、生成されるすべての値を受け取る必要があるため、これは絶対に必要でした。

その後、Common LispでeBay XML APIクライアントを記述しようとしましたが、HTMLPragのようなものがないことがわかりました。HTMLPragは便利です。私はそのプロジェクトをラケットでやることになりました。私は、ラケットのLiterate Programming機能を実験しましたが、私が地球上で、適切に記述された読み書き可能なコードを編集するのが通常のコードよりも難しいか、不適切に記述された「過剰なコメント」の読み書き可能なコードを見つける唯一のプログラマであることを発見しました。

私の新しいプロジェクトはCommon Lispで行われていますが、これは正しい選択でした。Racketコミュニティはこのプロジェクトに不可欠な並列性を信じていないからです。私がラケットから逃したかもしれないと思った唯一のことは、継続でした。しかし、再起動を使用することで必要なことを行うことができ、振り返ってみると、おそらく単純なクロージャーでそれを行うことができたでしょう。


2
自分で試したことはありませんが、Emacsでコマンドラインラケットプログラムを使用している人々からのブログ投稿を見てきました。例:bc.tech.coop/scheme/scheme-emacs.htm
ラリーコールマン

5
公平を期すと、慣用的なScheme POVから物事にアプローチしようとするのではなく、CLを書きたいというSchemeに来たようです。たとえば、スキームはループを使用するのではなく再帰を奨励しませんか?
そり14

@ArtB Schemeは再帰を奨励するだけでなく、それを必要とするので、もちろん上記のプロジェクトでは大量の再帰を使用しました。そして、それはちょうど繰り返しを追加するのに役立ちました(たとえば、フォームのすべてのブランチに再帰呼び出しのコピーを含める必要がありますcond)およびバグ(その時点でループ終了テストを正しく記述しましたか?)今日でも、印象を受けますラケットは主に学生向けであり、プロのプログラマー向けではありません。私以外の誰かがそれを使用していると聞くたびに、彼らは「Beginning Student」サブ言語を使用しており、それはクラス用です。
アカウントを捨てる

CONDをまたいでコードを繰り返している場合、別の関数が必要なだけですか?
そり

@ArtB異なる引数でループ関数を呼び出す関数?それは無意味です。ほぼすべてのSchemeコードでこの種の繰り返しが見られます。ラケット独自のソースコードにも例があります。
スローアウェイアカウント

5

Schemeは、別個のコンパイルを念頭に置いて設計されています。その結果、貧弱で衛生的なマクロシステムを制限するのではなく、Common Lispスタイルのdefmacroを許可する拡張機能を使用しても、そのマクロの力はしばしば厳しく制限されます。次のコード行ですぐに使用することを目的とした別のマクロを定義するマクロを常に定義できるとは限りません。そして、そのような可能性は、効率的なeDSLコンパイラを実装するために不可欠です。

言うまでもなく、R5RS衛生マクロのみを使用するScheme実装は、メタプログラミングスタイルを衛生に適切に変換できないため、ほとんど役に立ちません。

幸いなことに、その制限のないScheme実装(ラケットなど)があります。


1
こんにちは、私は最近Racketを使ってSchemeに慣れ始めました。ラケットで非衛生的なマクロを使用する簡単な例を提供していただけますか?利用可能なマクロのタイプは、CLとSchemeの間で最も熱く議論されているポイントの1つのようです。
orange80

@ orange80、1つのアプローチはdocs.racket-lang.org/mzlib/mzlib_defmacro.htmlを使用することです。そしてもちろん、R6RSモードではそれほど制限のない方法があります。
SKロジック

@ SK-logicあなたはとても非衛生的なマクロで何をしているのですか?
そり14

1
@ArtB、私はeASTをコンパイラー関数として実装しており、ソースASTで非常に多くのことができます。衛生は、このようなアプローチ内の完全な迷惑です。どのように動作するかを見ることができます:github.com/combinatorylogic/mbase
SK-logic
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.