Para mí, parece que siempre puedes pasar argumentos de función en lugar de usar una clase de tipos. Por ejemplo, en lugar de definir la clase de tipo de igualdad:
class Eq a where
(==) :: a -> a -> Bool
Y usarlo en otras funciones para indicar el argumento de tipo debe ser una instancia de Eq
:
elem :: (Eq a) => a -> [a] -> Bool
¿No podemos simplemente definir nuestra elem
función sin usar una clase de tipos y en su lugar pasar un argumento de función que haga el trabajo?
Monad m
restricción me dice más que pasar argumentos de función adicionales de tiposa -> m a
ym a -> (a -> m b) -> m b
.TypeApplications
extensión le permite hacer explícito el argumento implícito.(==) @Int 3 5
compara3
y5
específicamente comoInt
valores. Puede pensar@Int
como una clave en el diccionario de funciones de igualdad específicas de tipo, en lugar de laInt
función de comparación específica en sí.Respuestas:
Si. Esto se llama "estilo de aprobación de diccionario". A veces, cuando estoy haciendo algunas cosas especialmente difíciles, necesito desechar una clase de texto y convertirla en un diccionario, porque la aprobación del diccionario es más potente 1 , pero a menudo bastante engorrosa, lo que hace que el código conceptual simple parezca bastante complicado. A veces uso el estilo de aprobación de diccionario en idiomas que no son Haskell para simular clases de tipos (pero he aprendido que, por lo general, no es una idea tan buena como parece).
Por supuesto, cada vez que hay una diferencia en el poder expresivo, hay una compensación. Si bien puede usar una API determinada de más maneras si está escrita con DPS, la API obtiene más información si no puede. Una forma en que esto aparece en la práctica es en
Data.Set
, que se basa en el hecho de que solo hay unOrd
diccionario por tipo. LasSet
tiendas de sus elementos ordenados segúnOrd
, y si se construye un conjunto con un diccionario, y luego inserta un elemento utilizando una diferente, ya que sería posible con DPS, se podría romperSet
's invariante y hacer que se caiga. Este problema de singularidad puede mitigarse utilizando un fantasma existencialescriba para marcar el diccionario, pero, una vez más, a costa de una complejidad bastante molesta en la API. Esto también se muestra más o menos de la misma manera en laTypeable
API.El bit de singularidad no aparece muy a menudo. En qué clases de tipos es excelente escribir código para usted. Por ejemplo,
que toma dos "procesadores" que toman una entrada y pueden dar una salida, y los concatena, aplanándolos
Nothing
, tendría que escribirse en DPS algo como esto:Esencialmente tuvimos que deletrear el tipo en el que lo estamos usando nuevamente, a pesar de que ya lo deletreamos en la firma de tipo, e incluso eso era redundante porque el compilador ya conoce todos los tipos. Debido a que solo hay una forma de construir un determinado
Semigroup
en un tipo, el compilador puede hacerlo por usted. Esto tiene un efecto de tipo "interés compuesto" cuando comienzas a definir muchas instancias paramétricas y a usar la estructura de tus tipos para calcular por ti, como en losData.Functor.*
combinadores, y esto se usa para un gran efecto con elderiving via
que esencialmente puedes obtener todos los estructura algebraica "estándar" de su tipo escrita para usted.Y ni siquiera me hagas comenzar con MPTC y fundeps, que retroalimentan la información en la verificación de tipos e inferencia. Nunca he intentado convertir tal cosa a DPS; sospecho que implicaría pasar muchas pruebas de igualdad de tipos, pero en cualquier caso, estoy seguro de que sería mucho más trabajo para mi cerebro de lo que estaría cómodo con.
-
1 U alvo que utilizar
reflection
en cuyo caso se convierten en equivalente en potencia - peroreflection
también puede ser complicado de usar.fuente
Si. Eso (llamado pasar de diccionario) es básicamente lo que hace el compilador para escribir clases de todos modos. Para esa función, hecha literalmente, se vería un poco así:
Llamar
elemBy (==) x xs
ahora es equivalente aelem x xs
. Y en este caso específico, puede ir un paso más allá:eq
tiene el mismo primer argumento cada vez, por lo que puede hacer que la responsabilidad de la persona que llama aplique eso y termine con esto:Llamar
elemBy2 (x ==) xs
ahora es equivalente aelem x xs
....Oh espera. Eso es justo
any
. (Y, de hecho, en la biblioteca estándar,.elem = any . (==)
)fuente
implicit
y el compilador los inyectaría desde el alcance.