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 elemfunción sin usar una clase de tipos y en su lugar pasar un argumento de función que haga el trabajo?

Monad mrestricción me dice más que pasar argumentos de función adicionales de tiposa -> m aym a -> (a -> m b) -> m b.TypeApplicationsextensión le permite hacer explícito el argumento implícito.(==) @Int 3 5compara3y5específicamente comoIntvalores. Puede pensar@Intcomo una clave en el diccionario de funciones de igualdad específicas de tipo, en lugar de laIntfunció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 unOrddiccionario por tipo. LasSettiendas 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 laTypeableAPI.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
Semigroupen 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 viaque 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
reflectionen cuyo caso se convierten en equivalente en potencia - peroreflectiontambié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 xsahora es equivalente aelem x xs. Y en este caso específico, puede ir un paso más allá:eqtiene 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 ==) xsahora es equivalente aelem x xs....Oh espera. Eso es justo
any. (Y, de hecho, en la biblioteca estándar,.elem = any . (==))fuente
implicity el compilador los inyectaría desde el alcance.