Me las arreglé para entender la definición de la clase. MonadReader
class Monad m => MonadReader r m | m -> r where
...
Después de leer el documento de Dependencia funcional en Haskell, ahora puedo entender que | m -> r
especifica que la variable de tipo r
se decide de forma exclusiva m
. Creo que este requisito es razonable en función de las pocas instancias típicas de MonadReader que he visto hasta ahora (por ejemplo Reader
), pero me parece que todavía podemos definir instancias como Reader
incluso sin esta cláusula de dependencia funcional.
Mi pregunta es ¿por qué necesitamos dependencia funcional en la definición de MonadReader? ¿Es esto funcionalmente necesario para definir MonadReader en el sentido de que MonadReader no se puede definir adecuadamente sin él, o es simplemente una restricción para limitar las formas en que MonadReader puede usarse para que las instancias de MonadReader se comporten de una manera esperada?
fuente
MonadReader
; Es necesario para usar convenientementeMonadReader
.Respuestas:
Es necesario para que la inferencia de tipos funcione de una manera que sea más conveniente para el usuario.
Por ejemplo, sin el fundep esto no compilaría:
Para hacer la compilación anterior necesitaríamos escribir
Esto es porque, sin el fundep, el compilador no puede inferir que
x
es unInt
. Después de todo, una mónadaReadertT Int IO
podría tener múltiples instanciasentonces el programador debe proporcionar alguna anotación que fuerce
x :: Int
, o el código es ambiguo.fuente
Esto no es realmente una respuesta, pero es demasiado largo para un comentario. Tienes razón en que es posible definir la
MonadReader
clase sin un fundep. En particular, la firma de tipo de cada método determina cada parámetro de clase. Sería bastante posible definir una jerarquía más fina.El principal problema con este enfoque es que los usuarios tienen que escribir un montón de instancias.
fuente
Creo que la fuente de confusión es que en la definición de
Se asume implícitamente que se
m
contiene ar
sí mismo (para instancias comunes). Déjame usar una definición más ligera deReader
comoCuando
r
se elige el parámetro, puede definir fácilmente una instancia de mónada paraReader r
. Eso significa que en la definición de clase de tipo sem
debe sustituirReader r
. Así que mira cómo la expresión termina siendo:Pero, ¿por qué necesitamos esto? Mira la definición de
ask
dentro de laMonadReader
clase.Sin el fun-dep nada podría detenerme por definir
ask
una forma de devolver un tipo diferente como el estado. Aún más, podría definir muchas instancias de lector de mónada para mi tipo. Como ejemplo, estas serían definiciones válidas sin func-depEntonces, si tuviera un valor,
val :: ReaderT Int IO Double
¿cuál sería el resultadoask
? Tendríamos que especificar una firma de tipo como se muestra a continuaciónAdemás de no tener sentido, no es conveniente especificar el tipo una y otra vez.
Como conclusión, utilizando la definición real de
ReaderT
. Cuando tiene algo como queval :: ReaderT String IO Int
la dependencia funcional dice: Tal tipo podría tener solo una instancia única deMonadReader
typeclass que se define como la que usaString
comor
fuente