Подтвердить что ты не робот

Синтаксис записи Haskell

Синтаксис записи Haskell многими считается бородавкой на иначе элегантном языке из-за его уродливого синтаксиса и загрязнения пространства имен. С другой стороны, это часто более полезно, чем альтернатива, основанная на позиции.

Вместо объявления вроде этого:

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

Мне кажется, что что-то в этом направлении было бы более привлекательным:

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

Я уверен, что должна быть хорошая причина, по которой я отсутствую, но почему синтаксис C-подобной записи был принят более чистым макетным подходом?

Во-вторых, есть ли что-нибудь в конвейере для решения проблемы пространства имен, поэтому мы можем написать id foo вместо fooID foo в будущих версиях Haskell? (В дополнение к обходным методам, основанным на методе longwinded type class, доступных в настоящее время.)

4b9b3361

Ответ 1

Хорошо, если никто другой не попытается, я возьму другой (чуть более тщательно изученный) удар, отвечая на эти вопросы.

TL;DR

Вопрос 1: Это просто способ, которым играли кости. Это был косвенный выбор, и он застрял.

Вопрос 2: Да (сортировка). Несколько разных сторон, безусловно, думали о проблеме.

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

Почему синтаксис C-подобной записи был применен в более чистом макетном подходе?

Исследователи Microsoft написали History of Haskell. Раздел 5.6 рассказывает о записях. Я процитирую первый крошечный бит, который проницателен:

Одно из самых очевидных упущений ранних версий Haskell было отсутствие записей, предлагающих названные поля. При условии записи чрезвычайно полезны на практике, почему они были опущены?

Затем Microsoft ответят на свой вопрос

Самая сильная причина, по-видимому, заключалась в отсутствии очевидного "правильного" дизайна.

Вы можете сами прочитать статью, но они говорят, что Haskell в конечном итоге принял синтаксис записи из-за "давления на именованные поля в структурах данных".

К моменту разработки Haskell 1.3 в 1993 году пользователь давление для названных полей в структурах данных было сильным, поэтому комитет в конечном итоге принял минималистский дизайн...

Вы спрашиваете, почему именно? Ну, из того, что я понимаю, если бы ранние Haskellers имели свой путь, у нас, возможно, никогда не было синтаксиса записи. Идея, по-видимому, была направлена ​​на Haskell людьми, которые уже привыкли к синтаксису типа C, и больше интересовались тем, что C-подобные вещи попадали в Haskell, а не делали "путь Haskell". (Да, я понимаю, что это очень субъективная интерпретация. Я могу быть мертв неправильно, но в отсутствие лучших ответов это лучший вывод, который я могу сделать.)

Есть ли что-нибудь в конвейере для решения проблемы с пространством имен?

Прежде всего, не все чувствуют, что это проблема. Несколько недель назад энтузиаст Racket объяснил мне (и другим), что наличие разных функций с тем же именем было плохой идеей, потому что это усложняет анализ "что делает функция ___?"? На самом деле это не одна функция, а много. Идея может быть сложной для Haskell, поскольку она усложняет вывод типа.

По небольшим касаниям, у Microsoft есть интересные вещи, которые можно сказать о машинных классах Haskell:

Это было счастливое совпадение времени, в течение которого Вадлер и Блотт сумели создать эту ключевую идею в тот момент, когда дизайн языка все еще находился в потоке.

Не забывайте, что Хаскелл был молод один раз. Некоторые решения были сделаны просто потому, что они были сделаны.

В любом случае есть несколько интересных способов, с помощью которых можно было справиться с этой "проблемой":

Тип Directed Name Resolution, предлагаемая модификация Haskell (упомянутая в комментариях выше). Просто прочитайте эту страницу, чтобы увидеть, что она затрагивает множество областей языка. В общем, это неплохая идея. В него было заложено много идей, чтобы он не столкнулся с вещами. Тем не менее, он все равно потребует значительно большего внимания, чтобы превратить его в современный (более) зрелый язык Haskell.

Еще одна статья Microsoft, OO Haskell, специально предлагает расширение языка Haskell для поддержки "ad hoc overloading". Это довольно сложно, поэтому вам просто нужно проверить Раздел 4 для себя. Суть его заключается в том, чтобы автоматически (?) Выводить типы "Имеет" и добавлять дополнительный шаг к типу проверки, который они называют "улучшением", смутно изложенным в следующих избирательных цитатах:

Учитывая ограничение класса Has_m (Int -> C -> r), существует только один экземпляр для m, который соответствует этому ограничению... Поскольку есть только один выбор, мы должны сделать это сейчас, а это в свою очередь исправляет r как Int. Следовательно, мы получаем ожидаемый тип для f: f :: C -> Int -> IO Int... [это] просто выбор дизайна и один, основанный на идее, что класс Has_m закрыт

Извинения за некогерентное цитирование; если это поможет вам вообще, тогда здорово, иначе просто прочитайте газету. Это сложная (но убедительная) идея.

Chris Done использовал Template Haskell для предоставления утиного набора текста в Haskell в том же виде, что и бумага OO Haskell (используя типы "Has" ). Несколько интерактивных образцов сеансов с его сайта:

λ> 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*

Для этого требуется небольшой шаблонный/необычный синтаксис, и я лично предпочел бы придерживаться typeclasses. Но, к счастью, Крис Готол за то, что он свободно публикует свою работу в этом районе.

Ответ 2

Я просто подумал, что добавлю ссылку на проблему с пространством имен. Похоже, что перегруженные поля записи для GHC входят в GHC 7.10 (и, вероятно, уже находятся в HEAD), используя OverloadedRecordFields расширение.

Это позволит использовать синтаксис, например

data Person = Person { id :: Int, name :: String }
data Company { name :: String, employees :: [Person] }

companyNames :: Company -> [String]
companyNames c = name c : map name (employees c)

Ответ 3

[edit] Этот ответ - это всего лишь несколько случайных мыслей по этому вопросу. Я рекомендую другой ответ на этот вопрос, потому что для этого ответа я взял намного больше времени, чтобы посмотреть и обратиться к другим людям.

Синтаксис записи

Принимая несколько ударов в темноте: ваш предложенный синтаксис "на основе макета" очень похож на декларации без записи-синтаксиса 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 класса с помощью Prelude 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"}

Это отличные ответы, но они самые лучшие. Я лично не думаю, что синтаксис записи не уродлив. Я чувствую, что есть место для улучшения с использованием материалов namespacing/modules, но я понятия не имею, как сделать это лучше.