Сопоставление с образцом по типу полиморфного параметра - альтернативы

Допустим, мне нужны разные выходные данные в зависимости от типа полиморфного параметра функции. Моя первоначальная попытка не удалась с каким-то загадочным сообщением об ошибке:

choice :: a -> Int
choice (_ :: Int) = 0
choice (_ :: String) = 1
choice _ = 2

Однако мы можем легко это исправить, обернув нужные типы в разные конструкторы данных и используя их при сопоставлении с образцом:

data Choice a = IntChoice Int | StringChoice String | OtherChoice a

choice :: Choice a -> Int
choice (IntChoice _) = 0
choice (StringChoice _) = 1
choice (OtherChoice _) = 2

Вопрос: знаете ли вы способ обойти это? Есть ли функция в Haskell2010, GHC или любом из расширений, которая позволяет мне использовать первый вариант (или что-то подобное)?

2 ответа

Решение

Вопрос: знаете ли вы способ обойти это? Есть ли функция в Haskell2010, GHC или любом из расширений, которая позволяет мне использовать первый вариант (или что-то подобное)?

Нет, ни в Haskell 2010, ни в каком-либо расширении GHC нет функции, позволяющей написать функцию типа

choice :: a -> Int

чье возвращаемое значение зависит от типа его аргумента. Вы можете рассчитывать на такую ​​функцию, никогда не существующую в будущем.

Даже имея хаки для проверки внутреннего представления данных GHC во время выполнения, невозможно различить значение типа Int от значения, тип которого является новым типом Int: эти типы имеют идентичные представления времени выполнения.

Int Ваша функция возвращает значение, которое существует во время выполнения, поэтому оно должно быть определено другим значением, которое существует во время выполнения. Это может быть

  1. обычное значение, как в вашем data Choice a = IntChoice Int | StringChoice String | OtherChoice a; choice :: Choice a -> Int пример или

  2. словарь классов типов, использующий либо собственный класс, как в ответе Дэвида Янга, либо встроенный Typeable учебный класс:

    choice :: Typeable a => a -> Int
    choice a
      | typeOf a == typeOf (undefined :: Int)    = 0
      | typeOf a == typeOf (undefined :: String) = 1
      | otherwise                                = 2
    

Это смешивает два разных вида полиморфизма. То, что вы хотите, это специальный полиморфизм, который осуществляется с помощью классов типов. Вид полиморфизма функции типа a -> Int это параметрический полиморфизм. С параметрическим полиморфизмом, одно определение функции для choice должен работать для любого возможного типа a, В этом случае это означает, что он не может использовать значение типа a так как он ничего не знает об этом, так choice должен быть постоянной функцией, такой как choice _ = 3, На самом деле это дает вам очень сильные гарантии того, что может сделать функция, просто посмотрев на ее тип (это свойство называется параметричностью).

С помощью класса типов вы можете реализовать свой пример следующим образом:

class ChoiceClass a where
  choice :: a -> Int

instance ChoiceClass Int where
  choice _ = 0

instance ChoiceClass String where
  choice _ = 1

instance ChoiceClass a where
  choice _ = 2

Теперь я должен отметить, что этот подход класса типов часто неправильный, особенно когда кто-то, кто только начинает, хочет использовать его. Вы определенно не хотите делать это, чтобы избежать простого типа, как Choice введите свой вопрос. Это может добавить много сложности, и разрешение экземпляра может сначала сбить с толку. Обратите внимание: чтобы заставить работать решение класса типов, необходимо включить два расширения: FlexibleInstances а также TypeSynonymInstances поскольку String это синоним [Char], OverlappingInstances также необходимо, потому что классы типов работают в предположении "открытого мира" (это означает, что каждый может позже прийти и добавить экземпляр для нового типа, и это необходимо учитывать). Это не обязательно плохо, но здесь это признак ползучей сложности, вызванной использованием решения класса типов по сравнению с гораздо более простым решением типа данных. OverlappingInstances в частности, может сделать вещи сложнее думать и работать.

Другие вопросы по тегам