Haskell - Instancia automática de Monad


8

Estoy tratando de crear mi propio tipo de datos, que formará parte de la clase Monad, pero

newtype Container a = Container a deriving Monad

me da este error:

   * Can't make a derived instance of `Monad Container'
        (even with cunning GeneralizedNewtypeDeriving):
        cannot eta-reduce the representation type enough
    * In the newtype declaration for `Container'
   |
30 | newtype Container a = Container a deriving Monad

Funciona bien para otras clases (Mostrar, por ejemplo), pero no para Monad, entonces, ¿cómo puedo convencer a ghci de que instale mi clase Container to Monad?

Gracias


1
El problema es que ano es una instancia de mónada, por lo tanto, no tiene mucho sentido. Si, por ejemplo, lo usa newtype Container a = Container [a] deriving (Functor, Applicative, Monad), funcionará, ya que []es una instancia de Monad.
Willem Van Onsem

2
GenerlizedNewtypeDerivinges específicamente para "levantar" las instancias del tipo envuelto al nuevo tipo. La pregunta de cómo (o si) uno puede derivar automáticamente una Monadinstancia para Containertodavía es interesante. (El hecho que basedefine la Monadinstancia para Identityexplícito sugiere que no puede)
Chepner

Monadno es una de las clases de tipos que el estándar Haskell pone a disposición para ser derivadas automáticamente ( Showes, junto con algunas otras básicas). Sin embargo, creo que GHC puede hacerlo con las extensiones correctas.
Robin Zigmond

@RobinZigmond Tenga en cuenta que el mensaje indica que GeneralizedNewtypeDerivingestá habilitado, y una pregunta es por qué todavía no funciona.
Alexey Romanov

Respuestas:


9

Funciona bien para otras clases (Mostrar, por ejemplo)

Solo un conjunto fijo de clases estándar admite la derivación de fábrica:

En Haskell 98, las únicas clases derivables son Eq, Ord, Enum, Ix, Bounded, Read y Show. Varias extensiones de idioma amplían esta lista.

--- El manual de usuario de GHC

En particular Monadno pertenece a esa lista, ni a la extendida.

Hay más extensiones que generalizan derivando a clases arbitrarias, pero no pueden ser 100% automatizadas. Alguien en algún lugar tiene que especificar cómo se debe hacer esa derivación; Dependiendo de la clase, se puede requerir que el usuario cargue con la carga porque hay información que fundamentalmente no se puede inferir.

En su caso, el nuevo tipo Containeres representacionalmente equivalente a la Identitymónada en la biblioteca estándar, por lo que puede usar DerivingVia:

{-# LANGUAGE DerivingVia #-}
import Data.Functor.Identity

newtype Container a = Container a deriving (Functor, Applicative, Monad) via Identity

Solo hay una instancia sensata en esta situación muy particular, pero la mayoría de las veces no es fácil saber cuál debería ser la instancia, incluso si solo hay una.


También debe derivar Functory Applicative, pero luego comparar el tipo de Container 3 >>= (+1)con Identity 3 >>= (+1). No sé si eso está relacionado DerivingViao no.
Chepner

(En caso de que esté haciendo algo extraño, entiendo Container 3 >>= (+ 1) :: Num (Container b) => Container by Identity 3 >>= (+ 1) :: Num b => Identity b. No estoy seguro de por qué Container b, en lugar de b, tiene la Numrestricción.)
Chepner

Gracias por la precisión En cuanto a tu segundo comentario, para tener (+ 1) :: Num c => c -> ccomo flecha de Kleisli (+ 1) :: a -> Container bnecesitas unificar c ~ Container b. Pero no estoy seguro de cuál es su punto de partida.
Li-yao Xia

Me pregunto qué se define para lo Identityque no se define Container, según Identity 3 >>= (+1)evalúa Identity 4.
chepner

Es solo porque hay un instance Num a => Num (Identity a)definido.
KA Buhr
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.