Специализация типов функций в Haskell - PullRequest
2 голосов
/ 27 марта 2012

У меня есть функция с типом Read a => String -> a, возможно ли иметь другую функцию с тем же именем, которая работает по-другому, когда a, например, String? Существуют ли расширения GHC, которые позволяют это?

Что-то вроде:

f :: Read a => String -> a
f = read

f :: String -> String
f = id

Ответы [ 2 ]

12 голосов
/ 27 марта 2012

В Haskell такая перегрузка функций (специальный полиморфизм) достигается с помощью классов типов, а не путем привязки одного и того же имени к нескольким типам.

{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}

class F a where f :: String -> a

instance F String where f = id
instance F Int where f = read
instance F Char where f = read
instance F Float where f = read
-- etc.

Теперь f может работать с любым типом, для которого был объявлен экземпляр F.

К сожалению, вы не можете сойти с рук:

instance Read a => F a where f = read

Возможно, неинтуитивно, это не объявляет экземпляр F только для типов, которые имеют экземпляр Read. Поскольку GHC разрешает экземпляры, используя только заголовок объявления экземпляра (часть справа от =>), это фактически объявляет все типы a как экземпляры F, но делает ошибку типа для вызова f на что-либо, что не является экземпляром Read.

Он скомпилируется, если вы включите расширение UndecidableInstances, но это только приводит к другим проблемам. Это кроличья нора, которую ты не хочешь рисковать.

Вместо этого вы должны объявить экземпляр F для каждого отдельного типа, над которым вы собираетесь работать f. Это не очень обременительно для простого класса, такого как этот, но если вы используете последнюю версию GHC, вы можете использовать следующее, чтобы сделать его немного легче:

{-# LANGUAGE DefaultSignatures #-}

class F a where f :: String -> a
                default f :: Read a => String -> a
                f = read

Теперь для любого типа, который является экземпляром Read, вы можете объявить его экземпляр F без необходимости явно указывать реализацию f:

instance F Int
instance F Char
instance F Float
-- etc.

Для любых типов без экземпляров Read вам все равно придется написать явную реализацию для f.

4 голосов
/ 27 марта 2012

Я получил его на работу, но мне пришлось включить кучу сомнительных вариантов языка:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE OverlappingInstances #-}

class SpecialRead a where
  specialRead :: String -> a

instance Read a => SpecialRead a where
  specialRead = read

instance SpecialRead String where
  specialRead = id

main = do
    print (specialRead "10" :: Int)
    print (specialRead "10" :: String)
...