Haskell - instância automática de Mônada

8

Estou tentando criar meu próprio tipo de dados, que fará parte da classe Monad, mas

newtype Container a = Container a deriving Monad

me dá este erro:

   * 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 bem para outras classes (Show, por exemplo), mas não para o Monad, então como convencer o ghci a instanciar minha classe Container to Monad?

obrigado

Edward Gray
fonte
1
O problema é que anão é um exemplo de mônada, portanto, não faz muito sentido. Se você, por exemplo, usasse newtype Container a = Container [a] deriving (Functor, Applicative, Monad), funcionará, já que []é uma instância de Monad.
Willem Van Onsem
2
GenerlizedNewtypeDerivingé especificamente para "elevar" as instâncias do tipo agrupado para o novo tipo. A questão de como (ou se) alguém pode derivar automaticamente uma Monadinstância Containerainda é interessante. (O fato de basedefinir a Monadinstância para Identityexplícito sugere que você não pode.)
Chepner
Monadnão é uma das classes de tipo que o padrão Haskell disponibiliza para ser derivada automaticamente ( Showé, juntamente com algumas outras básicas). O GHC pode fazer isso com as extensões corretas, acredito.
Robin Zigmond
@RobinZigmond Observe que a mensagem indica GeneralizedNewtypeDerivingestá ativada e uma pergunta é por que ainda não funciona.
Alexey Romanov

Respostas:

9

Funciona bem para outras classes (mostrar por exemplo)

Somente um conjunto fixo de classes padrão suporta derivações prontas para uso:

No Haskell 98, as únicas classes deriváveis ​​são Eq, Ord, Enum, Ix, Bounded, Read e Show. Várias extensões de idioma estendem esta lista.

--- Manual do Usuário do GHC

Em particular Monad, não pertence a essa lista, nem a extendida.

Existem mais extensões que generalizam a derivação de classes arbitrárias, mas elas não podem ser 100% automatizadas. Alguém em algum lugar precisa especificar como essa derivação deve ser feita; dependendo da classe, o usuário pode ser obrigado a carregar o fardo, porque há informações que fundamentalmente não podem ser inferidas.

No seu caso, o newtype Containeré representacionalmente equivalente à Identitymônada na biblioteca padrão, para que você possa usar DerivingVia:

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

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

Há apenas uma instância sensata nessa situação muito específica, mas na maioria das vezes não é fácil dizer qual deve ser a instância, mesmo que exista apenas uma.

Li-yao Xia
fonte
Você também deve derivar Functore Applicative, mas depois comparar o tipo de Container 3 >>= (+1)com Identity 3 >>= (+1). Não sei se isso está relacionado DerivingViaou não.
chepner 28/02
(No caso de eu estar fazendo algo estranho, entendo Container 3 >>= (+ 1) :: Num (Container b) => Container be Identity 3 >>= (+ 1) :: Num b => Identity bnão sei por que Container b, ao invés disso b, tem a Numrestrição.)
chepner
Obrigado pela precisão. Quanto à sua segunda observação, para ter (+ 1) :: Num c => c -> ccomo uma flecha Kleisli, (+ 1) :: a -> Container bvocê precisa se unificar c ~ Container b. Mas não tenho certeza de qual é o seu ponto de partida.
Li-yao Xia
Eu só estou querendo saber o que está definido para o Identityque não está definido Container, como Identity 3 >>= (+1)avalia a Identity 4.
chepner 28/02
É apenas porque há um instance Num a => Num (Identity a)definido.
KA Buhr