58

Haskellのレコード構文は、その醜い構文と名前空間の汚染のために、他の点ではエレガントな言語に対するいぼであると多くの人に考えられています。一方で、ポジションベースの代替手段よりも便利なことがよくあります。

このような宣言の代わりに:

data Foo = Foo { 
  fooID :: Int, 
  fooName :: String 
} deriving (Show)

これらの線に沿った何かがより魅力的であるように私には思えます:

data Foo = Foo id   :: Int
               name :: String
               deriving (Show)

私が見逃しているのには十分な理由があるはずですが、なぜCのようなレコード構文がよりクリーンなレイアウトベースのアプローチに採用されたのでしょうか。

第二に、名前空間の問題を解決するためのパイプラインに何かがあるので、Haskellの将来のバージョンのid foo代わりに書くことができますか?fooID foo(現在利用可能な長蛇の型クラスベースの回避策は別として。)

4

4 に答える 4

54

他の誰も試みようとしないなら、私はこれらの質問に答えるために別の(もう少し慎重に調査した)突き刺しをします.

tl;dr

質問 1: これがサイコロの出方です。それは状況に応じた選択であり、立ち往生しました。

質問 2: はい (ちょっと)。確かに、いくつかの異なる政党がこの問題について考えていました。

関連性があり興味深いと思われるリンクと引用に基づいて、各回答の非常に長い説明を読んでください。

すっきりとしたレイアウトベースのアプローチではなく、C に似たレコード構文が採用されたのはなぜですか?

Microsoft の研究者は、Haskell の歴史に関する論文を書きました。セクション 5.6 では、レコードについて説明します。洞察に満ちた最初の小さな部分を引用します。

Haskell の初期のバージョンでの最も明白な省略の 1 つは、名前付きフィールドを提供するレコードがないことでした。記録は実際に非常に役立つのに、なぜ省略されたのですか?

次に、Microsofties は自分の質問に答えます

最も強い理由は、明確な「正しい」設計がなかったことにあると思われます。

詳細については論文を自分で読むことができますが、「データ構造内の名前付きフィールドに対するプレッシャー」のために、Haskell は最終的にレコード構文を採用したと述べています。

Haskell 1.3 の設計が進行中の 1993 年までには、データ構造の名前付きフィールドに対するユーザーの圧力が強かったため、委員会は最終的に最小限の設計を採用しました...

なぜそれがなぜなのかとあなたは尋ねますか?私の理解では、もし初期の Haskeller が思い通りになれば、そもそもレコード構文はなかったかもしれません。このアイデアは明らかに、すでに C のような構文に慣れていて、「Haskell のやり方」を行うよりも、C のようなものを Haskell に組み込むことに興味を持っていた人々によって Haskell に押し込まれました。(はい、これは非常に主観的な解釈であることは承知しています。完全に間違っている可能性がありますが、より良い答えがない場合、これが私が引き出すことができる最良の結論です。)

名前空間の問題を解決するためにパイプラインに何かありますか?

まず第一に、誰もが問題だと感じているわけではありません。数週間前、Racket愛好家が私 (および他の人) に、「___ という名前の関数は何をするのか?」の分析を複雑にするため、同じ名前で異なる関数を持つことは悪い考えだと説明しました。実際、それは 1 つの機能ではなく、多数の機能です。型推論が複雑になるため、このアイデアは Haskell にとって非常に厄介なものになる可能性があります。

少し関係がありましたが、Microsofties は Haskell の型クラスについて興味深いことを言っています。

Wadler と Blott がこの重要なアイデアを、言語設計がまだ流動的な時期にたまたま生み出したのは、幸運な偶然の一致でした。

Haskell がかつて若かったことを忘れないでください。一部の決定は、単に行われたという理由だけで行われました。

とにかく、この「問題」に対処できる興味深い方法がいくつかあります。

Type Directed Name Resolution、Haskell への提案された修正 (上記のコメントで言及)。そのページを読んで、言語の多くの領域に触れていることを確認してください. 全体として、それは悪い考えではありません。物とぶつからないように工夫が凝らされています。ただし、現在 (より) 成熟した Haskell 言語に組み込むには、さらに多くの注意が必要です。

Microsoft の別の論文OO Haskellでは、「アドホック オーバーロード」をサポートするための Haskell 言語の拡張を具体的に提案しています。かなり複雑なので、セクション 4 を自分で確認する必要があります。その要点は、「Has」型を自動的に (?) 推論し、「改善」と呼ばれる型チェックに追加のステップを追加することです。

クラスの制約が与えられたHas_m (Int -> C -> r)場合、この制約に一致する m のインスタンスは 1 つだけです...選択肢は 1 つだけなので、ここでそれを作成する必要があり、それが に修正rされますInt。したがって、期待される型を取得しますf: ...[this] は単に設計上の選択であり、クラスが閉じf :: C -> Int -> IO Intているという考えに基づいています。Has_m

一貫性のない引用をお詫びします。それが少しでも役に立てば、素晴らしいことです。それ以外の場合は、論文を読んでください。これは複雑な (しかし説得力のある) アイデアです。

Chris Done はテンプレート Haskell を使用して、Haskell でのダックタイピングを提供しています。彼のサイトからのいくつかのインタラクティブなセッションのサンプル:

λ> flap ^. donald
*Flap flap flap*
λ> flap ^. chris
I'm flapping my arms!

fly :: (Has Flap duck) => duck -> IO ()
fly duck = do go; go; go where go = flap ^. duck

λ> fly donald
*Flap flap flap*
*Flap flap flap*
*Flap flap flap*

これには定型文/珍しい構文が少し必要であり、個人的には型クラスに固執することを好みます。しかし、この分野で彼の現実的な作品を自由に公開してくれた Chris Done に敬意を表します。

于 2011-04-02T02:54:00.443 に答える
6

[編集] この回答は、この問題に関する私のランダムな考えです。この回答よりも他の回答をお勧めします。その回答では、他の人の作品を調べて参照するのにもっと時間がかかったからです。

構文を記録する

data暗闇の中でいくつかの刺し傷をとります。「レイアウトベースの」提案された構文は、非レコード構文宣言によく似ています。解析で混乱を招く可能性があります (?)

--record
data Foo = Foo {i :: Int, s :: String} deriving (Show)
--non-record
data Foo = Foo Int String deriving (Show)
--new-record
data Foo = Foo i :: Int, s :: String deriving (Show)

--record
data LotsaInts = LI {a,b,c,i,j,k :: Int}
--new-record
data LostaInts = LI a,b,c,i,j,k :: Int

後者の場合、正確には何に:: Int適用されますか? 全体のデータ宣言?

レコード構文を使用した宣言 (現在) は、構築および更新構文に似ています。これらの場合、レイアウトベースの構文は明確ではありません。=これらの余分な記号をどのように解析しますか?

let f1 = Foo {s = "foo1", i = 1}
let f2 = f1 {s = "foo2"}

let f1 = Foo s = "foo1", i = "foo2"
let f2 = f1 s = "foo2"

f1 s関数の適用ではなく、レコードの更新であることがどのようにわかりますか?

名前空間

idクラス定義の使用法をプレリュードの使用法と混ぜ合わせたい場合はどうしますidか? どちらを使用しているかをどのように指定しますか? 修飾されたインポートおよび/またはhidingキーワードよりも良い方法を考えられますか?

import Prelude hiding (id)

data Foo = Foo {a,b,c,i,j,k :: Int, s :: String}
               deriving (Show)

id = i

ghci> :l data.hs
ghci> let foo = Foo 1 2 3 4 5 6 "foo"
ghci> id foo
4
ghci> Prelude.id f1
Foo {a = 1, b = 2, c = 3, i = 4, j = 5, k = 6, s = "foo"}

これらは素晴らしい答えではありませんが、私が持っている最高のものです. 個人的には、レコード構文がそれほど醜いとは思いません。名前空間/モジュールについては改善の余地があると感じていますが、改善する方法がわかりません。

于 2011-03-20T17:41:16.817 に答える