Почему 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 года. Что-то изменилось недавно?
Даже если будет некоторая часть служебных обязанностей во время выполнения, большинство людей не возражают против того, что большинство кода вряд ли критично для производительности.
Есть ли скрытое расширение языка, которое на самом деле позволяет это? Опять я не уверен... но я думаю, что Идрис на самом деле это делает?
Ответы
Ответ 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. Ты прав: его глупо, что это не включено.