haskell - example - ¿Hay alguna buena razón por la que `deleteBy` no tenga su tipo más general?
spring data repository save (3)
Generalizar el tipo de deleteBy
viola el estándar de una manera muy práctica: los programas de Haskell perfectamente válidos se vuelven inválidos, gracias a la sobrecarga no resuelta.
Aquí hay una demostración:
class (Num a) => Magic a where
magic :: a -> Bool
sameMagic :: (Magic a, Magic b) => a -> b -> Bool
sameMagic a b = magic a == magic b
test :: (Magic a) => [a]
test = deleteBy sameMagic 42 [1234]
En Haskell, este programa está perfectamente bien tipado; El tipo restringido de deleteBy
asegura que se garantiza que el 42
tiene el mismo tipo que el 1234
. Con el deleteBy
generalizado, este no es el caso, por lo que el tipo 42
es ambiguo, lo que hace que el programa no sea válido. (Si desea un ejemplo menos artificial, considere una función que compara dos valores Integral
con toInteger
).
Por lo tanto, tal vez no haya una buena razón para este tipo restringido (aunque si se va a generalizar deleteBy
, preferiría la versión de Hammar a su propuesta), pero generalizarlo viola el estándar y puede romper programas válidos.
El informe de idiomas de Haskell 2010 establece en la sección 20.10.1.1 que:
deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a]
De hecho, la implementación en la biblioteca de GHC permitiría
deleteBy :: (b -> a -> Bool) -> b -> [a] -> [a]
pero en realidad restringe el tipo al anterior con la anotación.
Por lo tanto, uno no puede decir, por ejemplo:
foo = deleteBy fsteq 42 [(43, "foo"), (44, "bar"), (42, "baz")] where
fsteq a (b,_) = a == b
porque Int
no es lo mismo que (Int, String)
.
¿Hay alguna buena razón para esto?
La razón por la que pregunto es que, si no hay una buena razón para ello, incluiría deleteBy
con el tipo más general en el puerto de datos de Frege Lista que estoy haciendo actualmente. Pero tal vez estoy pasando por alto algo?
EDITAR: Como lo señaló @hammar, esto también se aplica a otras funciones xxx By.
Ingo,
en su pregunta original, parece que está preguntando por qué el Informe de Haskell especifica eliminar de esta forma. Entonces, si no hay una razón sólida, podría usar una definición diferente en Frege (lo que implica que no le importa la conformidad con el Informe Haskell).
Como dice Hammar, es como las otras funciones xxxBy: delete usos (==) , deleteBy toma un predicado similar a (==) : de tipo (a -> a -> Bool) y se supone que es una relación de equivalencia. Si bien el sistema de tipos no puede verificar si el predicado es realmente una relación de equivalencia, es la función de contrato. Así que es muy fácil entender lo que significa xxxBy si sabes lo que significa xxx. Y tal vez no sea el caso de deleteBy, pero en algunos casos una implementación podría optimizarse bajo el supuesto de que el predicado tiene las propiedades especificadas (relación de equivalencia o orden total, etc.).
Pero en su comentario a la respuesta de Hammar, pregunta si la implementación más general violaría el informe. Bueno, si el tipo es diferente, entonces es literalmente una violación, ¿verdad? Dado que los programas que no deben compilarse de acuerdo con el informe serán aceptados por su compilador. Por lo tanto, da lugar a un problema de portabilidad: si su código utiliza la versión más general, es posible que no se compile en otra implementación que se ajuste a la especificación. Además, elimina el requisito de relación de equivalencia.
Entonces, si desea una función más general, ¿por qué no simplemente define otra función con un nombre diferente? Por ejemplo, deleteIf .
(Quería comentar la respuesta de Hammar, pero no puedo, así que la escribí aquí).
Supongo que es por simetría con las otras funciones xxxBy
. Sin embargo, su tipo sigue siendo innecesariamente específico. Preferiría esto.
deleteBy :: (a -> Bool) -> [a] -> [a]
Luego puedes escribir tu ejemplo usando una aplicación parcial:
foo = deleteBy (fsteq 42) [(43, "foo"), (44, "bar"), (42, "baz")] where
fsteq a (b,_) = a == b