operadores - ¿Cómo maneja Haskell la sobrecarga de polimorfismo?
polimorfismo en haskell (6)
Aquí tienes un ejemplo simple que combina
Polimorfismo ad hoc (sobrecarga) : misma función con comportamiento diferente para diferentes tipos (por medio de clases de tipo Haskell)
Polimorfismo paramétrico : la misma función con el mismo comportamiento para diferentes tipos (por medio de una función parametrizada de tipo. En principio, el tipo no importa, pero usamos clases de tipo para restringir los tipos aceptables).
Código:
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)
Tengo una pregunta sobre el polimorfismo de Haskell.
Como he aprendido, hay dos tipos de polimorfismo:
Parametric : donde no se especifica el tipo de entrada.
Ejemplo:
functionName :: [a] -> a
Sobrecarga : como programación imperativa, es decir, pasar diferentes argumentos a la misma función.
Mi problema es: ¿cómo maneja Haskell la sobrecarga ?
Básicamente, la anulación es bastante diferente en Haskell, aunque puedes hacer algo similar.
I. Usa las clases como respuesta seleccionada.
class YesNo a where
yesno :: a -> Bool
Tienes que implementarlo usando una instancia, y luego puedes usarlo así:
> 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. Utilice la coincidencia de patrones del constructor de tipos, como:
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)
Y luego puedes usarlos así:
> 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. Tenga la función "sobrecargada" en el módulo separado, pero tendrá que prefijar el nombre de importación o el nombre de importación calificado
En algunos idiomas, la sobrecarga significa usar el mismo nombre para varias funciones que proporcionan una funcionalidad similar pero diferente, por lo que puede intentarlo
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
lo que le daría el error de Duplicate type signature
que está recibiendo.
Haskell no hace este tipo de sobrecarga, y un programador de Haskell le daría estos nombres diferentes:
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
La razón por la que Haskell no permite el tipo de sobrecarga que creo que estás preguntando es porque realmente no te permite hacer nada que no puedas hacer sin él, y permitirlo hace que sea casi imposible Tipos más avanzados de sobrecarga. La sobrecarga de Haskell es una herramienta muy poderosa; Infórmese sobre las clases de tipos y las clases de constructores para comenzar.
En realidad, dado que a los programadores de String
= [Char]
y Haskell les encanta la reutilización del código, es mucho más probable que escriban:
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
Aquí la Eq a
es un ejemplo de un tipo de sobrecarga que Haskell permite, donde splitOn
le permitirá dividir cualquier lista siempre que los elementos puedan ser comparados por igualdad (es decir, Haskell le permite definir su propia noción de igualdad). Puede usar esto para dividir una Cadena o, por ejemplo, una lista de Cadenas, pero no puede dividir una lista de funciones porque no puede verificar dos funciones para ver si son iguales. splitWith
es un ejemplo de Haskell que le permite tratar una función como la mayoría de los demás datos: ¡puede pasar una como argumento!
[Nota 1: las words
son una función estándar, splitWith
está en una biblioteca con una tipografía ligeramente diferente.]
[Nota 2: si realmente desea escribir estas funciones, aquí tiene cómo:
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)
]
Especifica el tipo de firma de su función en la clase de tipo original, luego crea varias instancias de esa función para las que escribió la firma. Por lo tanto, en el ejemplo publicado por Hammar, se puede pensar que un polimórfico es, y el tipo especificado en cada instancia (por ejemplo, Fooable Bool) como el tipo de (en este caso a es un Bool). Entonces, cuando llama a la función foo con un valor Bool, se llama a la instancia de Fooable para un valor Bool.
Por cierto, puede poner múltiples funciones en la clase de tipo, y puede definir funciones en términos de otras funciones definidas también.
p.ej
class Eq a where
(==), (/=) :: a -> a -> Bool
x /= y = not (x == y)
x == y = not (x /= y)
Puede que no sea obvio aquí, pero si define una instancia de Eq, solo necesita definir == o / =, no ambas, ya que están definidas en términos de la otra.
Haskell usa clases de tipos para el polimorfismo ad hoc.
La sobrecarga en Haskell se realiza mediante clases de tipo. Por ejemplo, supongamos que desea sobrecargar una función foo
que devuelve un Int
:
class Fooable a where
foo :: a -> Int
instance Fooable Int where
foo = id
instance Fooable Bool where
foo _ = 42
Sin embargo, son más poderosos que los mecanismos de sobrecarga que se encuentran en la mayoría de los idiomas. Por ejemplo, puede sobrecargar en el tipo de retorno:
class Barable a where
bar :: Int -> a
instance Barable Int where
bar x = x + 3
instance Barable Bool where
bar x = x < 10
Para más ejemplos, eche un vistazo a las clases de tipos predefinidas en Haskell.