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

Почему GHC Haskell не поддерживает перегруженные имена параметров записи?

Я говорю о том, что определить невозможно:

data A = A {name :: String}
data B = B {name :: String}

Я знаю, что GHC просто снимает это с простых функций, и идиоматический способ решить это будет:

data A = A {aName :: String}
data B = B {bName :: String}

class Name a where
  name :: a -> String

instance Name A where
  name = aName

instance Name B where
  name = bName

После того, как я написал это, мне не нравится, что... не может ли эта классификация быть частью процесса desugaring?


Эта мысль пришла ко мне, когда я писал синтаксический анализ Aeson JSON. Там, где было бы просто просто выводить экземпляры FromJSON для каждого типа данных, я должен был написать все вручную (в настоящее время > 1k строк и подсчет). Наличие таких имен, как name или просто value в записи данных, не так уж редко.

http://www.haskell.org/haskellwiki/Performance/Overloading упоминает, что перегрузка функций вводит некоторые служебные данные во время выполнения. Но я на самом деле не понимаю, почему компилятор не сможет решить это во время компиляции и дать им разные имена внутри.

Этот вопрос SO от 2012 года более или менее содержит исторические причины и указывает на почтовый поток с 2006 года. Что-то изменилось недавно?

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

Есть ли скрытое расширение языка, которое на самом деле позволяет это? Опять я не уверен... но я думаю, что Идрис на самом деле это делает?

4b9b3361

Ответ 1

Использование синтаксиса записи

data A { name :: String }

неявно определяет функцию

name :: A -> String

Если определены A и B с { name :: String }, у нас есть конфликтующие определения типов для name:

name :: A -> String
name :: B -> String

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

data A { name :: String }
data B { name :: Text }

то мы просто переместили проблему на конфликтующие определения класса типов:

class Has'name a where
     name :: a -> String

class Has'name a where
     name :: a -> Text

В принципе это может быть разрешено так или иначе, но это всего лишь один из нескольких сложных противоречивых желательных свойств для записей. Когда Haskell был определен, было решено, что лучше иметь простую, если ограниченную поддержку, а не пытаться создать что-то более амбициозное и сложное. Несколько улучшений записей были обсуждены в разное время, и есть многолетние обсуждения, например. этот поток Haskell Cafe. Возможно, что-то будет разработано для Haskell Prime.

Ответ 2

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

Вы можете "desugar"

data A { name :: String }
data B { name :: Text   }

в

class Has'name a b | a -> b where
    name :: a -> b

data A { aName :: String }
instance Has'name A String where
    name :: aName

data B { bName :: Text   }
instance Has'name B Text   where
    name :: bName

но для этого потребуются расширения GHC (функциональные зависимости), которые еще не ввели его в стандарт. Это исключало бы использование только "имени" для создания записей, обновлений и сопоставления шаблонов (шаблоны просмотра могли бы помочь там), поскольку "имя" не является "просто" функцией в этих случаях. Вероятно, вы можете снять что-то очень похожее с шаблоном Haskell.

Ответ 3

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

Haskell и GHC делают это легко, потому что весь анализатор Haskell доступен как обычная библиотека. Вы можете просто разобрать все файлы, выполнить эту схему переименования (например, "данные A (имя:: String)" и "let a = A" Betty "по имени a" в "данные A {a_Name:: String}" и "пусть a = A" Бетти "в aName a" ) в зависимости от типа данных, к которым применяется функция имени, с использованием распознавателя типа и записи для компиляции.

Но, честно говоря, это должно быть интегрировано в GHC. Ты прав: его глупо, что это не включено.