Я хочу создать список разных вещей, которые имеют одно свойство, а именно: они могут быть превращены в строку. Объектно-ориентированный подход прост: определить интерфейс Showable
и сделать его интересными. Вторая точка может в принципе быть проблемой, если вы не можете изменить классы, но пусть притворяется, что это не так. Затем вы создаете список Showable
и заполняете его объектами этих классов без каких-либо дополнительных помех (например, повышение уровня обычно выполняется неявно). Доказательство концепции в Java приведено здесь.
Мой вопрос в том, какие варианты для этого у меня есть в Haskell? Ниже я обсуждаю подходы, которые я пробовал и которые на самом деле не удовлетворяют меня.
Подход 1: существительные. Работает, но уродливо.
{-# LANGUAGE ExistentialQuantification #-}
data Showable = forall a. Show a => Sh a
aList :: [Showable]
aList = [Sh (1 :: Int), Sh "abc"]
Основным недостатком для меня здесь является необходимость Sh
при заполнении списка. Это очень похоже на upcast-операции, которые неявно выполняются на OO-языках.
В общем случае фиктивная обертка Showable
для вещи, которая уже находится в языке - Show
type class, добавляет дополнительный шум в мой код. Нехорошо.
Подход 2: impredicatives. Желательно, но не работает.
Самый простой тип для такого списка для меня и то, что я действительно желаю, будет:
{-# LANGUAGE ImpredicativeTypes #-}
aList :: [forall a. Show a => a]
aList = [(1 :: Int), "abc"]
Кроме того (как я слышал) ImpredicativeTypes
"хрупка в лучшем случае и сломана в худшем случае"
он не компилируется:
Couldn't match expected type ‘a’ with actual type ‘Int’
‘a’ is a rigid type variable bound by
a type expected by the context: Show a => a
и ту же ошибку для "abc"
. (Обратите внимание на подпись типа для 1: без нее. Я получаю еще более странное сообщение: Could not deduce (Num a) arising from the literal ‘1’
).
Подход 3: типы Rank-N вместе с некоторыми функциональными списками (списки различий?).
Вместо проблемного ImpredicativeTypes
можно было бы предпочесть более стабильный и широко принятый RankNTypes
. Это в основном означает: двигаться
желаемый forall a. Show a => a
из конструктора типа (т.е. []
) для простых типов функций. Следовательно, нам нужно некоторое представление списков как простых функций. Как я едва слышал, есть такие представления. Тот, о котором я слышал, - это списки различий. Но в Dlist
package основной тип - старый добрый data
, поэтому мы возвращаемся к impredicatives. Я еще не исследовал эту строку, поскольку я подозреваю, что она может привести к более подробному коду, чем к подходу 1. Но если вы считаете, что это не так, пожалуйста, дайте мне пример.
Нижняя строка: как бы вы атаковали такую задачу в Haskell? Не могли бы вы дать более сжатое решение, чем на языке OO (особенно вместо заполнения списка - см. Комментарий для кода в подходе 1)? Можете ли вы прокомментировать, насколько важны перечисленные выше подходы?
UPD (на основе первых комментариев): этот вопрос, конечно, упрощается с целью удобочитаемости. Реальная проблема заключается в том, как хранить вещи, которые имеют один и тот же тип, например, могут обрабатываться позже несколькими способами (Show
имеет только один метод, но другие классы могут иметь более одного). Это приводит к устранению решений, которые предлагают применить метод Show
прямо при заполнении списка.