Как Haskell справляется с перегрузочным полиморфизмом? - PullRequest
17 голосов
/ 09 июля 2011

У меня вопрос о полиморфизме Хаскеля.

Как я узнал, существует два типа полиморфизма:

  1. Параметрический : где вы не указываете тип ввода.

    Пример:

    functionName :: [a] -> a
    
  2. Перегрузка : как императивное программирование, то есть передача разных аргументов одной и той же функции.

Моя проблема: как Haskell обрабатывает перегрузку ?

Ответы [ 6 ]

43 голосов
/ 09 июля 2011

Перегрузка в Haskell выполняется с использованием классов типов. Например, допустим, вы хотите перегрузить функцию foo, которая возвращает Int:

class Fooable a where
    foo :: a -> Int

instance Fooable Int where
    foo = id

instance Fooable Bool where
    foo _ = 42

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

class Barable a where
    bar :: Int -> a

instance Barable Int where
    bar x = x + 3

instance Barable Bool where
    bar x = x < 10

Дополнительные примеры можно найти в классах предопределенных типов в Haskell.

6 голосов
/ 14 августа 2012

В некоторых языках перегрузка означает использование одного и того же имени для нескольких функций, которые предоставляют аналогичные, но разные функции, поэтому вы можете попробовать

split :: String -> [String]                      -- splits on whitespace
split :: Char -> String -> [String]              -- splits on the given character
split :: [Char] -> String -> [String]            -- splits on any of the given characters
split :: (Char -> Bool) -> String -> [String]    -- splits using a function that tells you when

, которая выдаст вам ошибку Duplicate type signature.

Haskell не выполняет этот тип перегрузки, и программист на Haskell дал бы следующие имена:

words :: String -> [String]                        -- splits on whitespace
splitOn :: Char -> String -> [String]              -- splits on the given character
splitsOn :: [Char] -> String -> [String]           -- splits on any of the given characters
splitWith :: (Char -> Bool) -> String -> [String]  -- splits using a function that tells you when

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

На самом деле, поскольку String = [Char] и программисты на Haskell любят повторное использование кода, они с гораздо большей вероятностью будут писать:

words :: String -> [String]                -- splits on whitespace
splitOn :: Eq a => a -> [a] -> [[a]]       -- splits on the given item
splitsOn :: Eq a => [a] -> [a] -> [[a]]    -- splits on any of the given items
splitWith :: (a -> Bool) -> [a] -> [[a]]   -- splits using a function that tells you when

Здесь Eq a является примером некоторой перегрузки, которую разрешает Haskell, где splitOn позволит вам разбить любой список, если элементы можно сравнивать на равенство (т. Е. Haskell позволяет вам определить собственное понятие равенства ). Затем вы можете использовать это для разделения String или, например, списка Strings, но вы не можете разделить список функций, потому что вы не можете проверить две функции, чтобы увидеть, равны ли они. splitWith - это пример Haskell, позволяющий вам обращаться с функцией точно так же, как с большинством других данных - вы можете передать ее в качестве аргумента!

[Примечание 1: words - стандартная функция, splitWith находится в библиотеке с несколько иной типизацией.]
[Примечание 2: если вы действительно хотите написать эти функции, вот как:

splitWith isSplitter list =  case dropWhile isSplitter list of
  [] -> []
  thisbit -> firstchunk : splitWith isSplitter therest
    where (firstchunk, therest) = break isSplitter thisbit

-- words = splitWith isSpace           -- not needed, standard function from the Prelude
splitOn c = splitWith (== c)           -- notice I passed == in an argument! 
splitsOn chars = splitWith (`elem` chars)

]

4 голосов
/ 09 июля 2011

Haskell использует классы типов для специального полиморфизма.

3 голосов
/ 21 августа 2011

Вы указываете сигнатуру вашей функции в исходном классе типов, затем создаете несколько экземпляров той функции, для которой вы написали сигнатуру.Таким образом, в опубликованном примере Hammar вы можете думать о a как о полиморфном, а тип, указанный в каждом экземпляре (например, Fooable Bool), как о типе (в данном случае a - Bool),Поэтому, когда вы вызываете функцию foo со значением Bool, вызывается экземпляр Fooable для значения Bool.

Кстати, вы можете поместить несколько функций в класс типа, и вы можетеопределить функции в терминах других функций, определенных также.

например,

class  Eq a  where
    (==), (/=)  ::  a -> a -> Bool

    x /= y  = not (x == y)
    x == y  = not (x /= y)

Здесь это может быть неочевидно, но если вы определяете экземпляр Eq, вам нужно только определить == или / =, а не оба, поскольку они определеныс точки зрения друг друга.

1 голос
/ 27 сентября 2012

В Хаскелле переопределение в корне совсем другое, хотя вы можете сделать что-то похожее.

I. Используйте Классы как выбранный ответ.

class YesNo a where  
    yesno :: a -> Bool  

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

> yesno $ length []  
False  
> yesno "haha"  
True  
> yesno ""  
False  
> yesno $ Just 0  
True  
> yesno True  
True  
ghci> yesno EmptyTree  
False  
> yesno []  
False  
> yesno [0,0,0]  
True  

http://learnyouahaskell.com/making-our-own-types-and-typeclasses

II. Используйте сопоставление с образцом конструктора типа, например:

data Shape = Circle Float Float Float | Rectangle Float Float Float Float 
surface :: Shape -> Float  
surface (Circle _ _ r) = pi * r ^ 2  
surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)

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

> surface $ Circle 10 20 10  
314.15927  
> surface $ Rectangle 0 0 100 100  
10000.0

http://learnyouahaskell.com/making-our-own-types-and-typeclasses

III. Включите функцию «oveloaded» в отдельном модуле, но вам нужно будет поставить префикс имени импорта или полного имени импорта

http://learnyouahaskell.com/modules

0 голосов
/ 18 октября 2015

Здесь у вас есть простой пример, объединяющий

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

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

Код:

import Data.Char

class MyTypeFamily t where
      f1 :: t -> Int
      f2 :: t -> Int

instance MyTypeFamily Int where
         f1 x = x*x
         f2 x = x+x

instance MyTypeFamily Char where
         f1 x = (ord x) * (ord x)
         f2 x = (ord x) + (ord x)

instance MyTypeFamily Bool where
         f1 x 
            | x = 10
            | otherwise = 10 
         f2 x 
            | x = 100
            | otherwise = -100

-- ...............................................................
-- using f1, f2 as "overloaded" functions ("ad-hoc polymorphism)
--  (the algorithm for f1, f2 is chosen depending on their type)
--
-- using  fun as polymorphic (parametric polymorphic function)
-- the algorithm of fun is always the same but it works on
-- different types
fun :: (MyTypeFamily t) => t -> Int
fun x = (f1 x) + (f2 x) 

-- ...............................................................
-- ...............................................................
main =  do
        print $  fun 'J'
        print $  fun True
        print $  fun False
        print $  fun (8 :: Int)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...