Estou tentando implementar algo genérico semelhante a um anel e aplicá-lo a uma estrutura de dados de música descrita por Paul Hudak no livro The Haskell School of Music. Naturalmente, há muitas travessuras de Semigrupo/Monóide que são omitidas, mas tenho o seguinte código relevante:
newtype Duo a b = Duo {duo1 :: a} -- A ring-like structure
data Song a =
Primitive a
| Song a :+: Song a -- Composing Music Sequentially
| Song a :=: Song a deriving Eq -- Composing Music Concurrently (in parallel)
instance Functor Song where
fmap f (x :+: y) = fmap f x :+: fmap f y
fmap f (x :=: y) = fmap f x :=: fmap f y
fmap f (Primitive x) = Primitive $ f x
newtype Concurrent a = Concurrent {fromConcurrent :: Song a} deriving (Show)
newtype Sequential a = Sequential {fromSequential :: Song a} deriving (Show)
type Music a = Duo (Maybe (Concurrent a)) (Maybe (Sequential a))
Estou tentando escrever uma instância do Functor for Music, como o Duo não tem um Functor, pensei que isso não seria um problema.
Escrevi a seguinte implementação:
instance Functor Music where
fmap :: (a -> b) -> Music a -> Music b
fmap f = Duo . fmap (fmap f . fromConcurrent) . duo1
Mas recebo o seguinte erro:
• The type synonym ‘Music’ should have 1 argument, but has been given none • In the instance declaration for ‘Functor Music’ | 167 | instance Functor Music where | ^^^^^^^^^^^^^
Talvez o problema seja apenas que estou essencialmente escrevendo um Functor apenas para um subconjunto de Duo, talvez isso só funcione se eu fizer da música um novo tipo em vez de apenas um sinônimo de tipo. Eu realmente espero evitar isso devido ao número já flagrante de wrappers acontecendo neste código, eu realmente odiaria adicionar outro. Talvez todos vocês possam pensar em uma maneira de implementar um functor para o Duo que faça sentido e faça tudo funcionar?
Uma coisa que eu realmente não entendo é por que vamos fazer uma instância para mostrar:
instance (Show a) => Show (Music a) where
show (Duo Nothing) = "Silence"
show (Duo (Just (Concurrent x))) = show x
Infelizmente, esse tipo de abstração em nível de tipo não é permitido em Haskell. Mas acho que você pode ter cometido algum tipo de erro conceitual em seu design de tipo de dados em algum lugar. Expandindo os tipos, como
Duo
tem apenas um campo, teríamos:Isso é realmente o que você pretendia?
No caso improvável de que seja, acho que ficaria muito tentado a cortar os intermediários.
Se este é o comportamento pretendido, mas você deve manter os intermediários,
newtype
é o caminho a seguir.No caso provável de que não era assim que você pretendia
Music
se comportar, provavelmente podemos lhe dar mais/melhores conselhos, mas precisaríamos saber mais sobre o que você pretendeDuo
dizer.Em relação à sua edição/comentário sobre
Show
: em suaShow
instância, você inclui um argumento de tipo paraMusic
. O problema daFunctor
instância é exatamente o fato de você não incluir o argumento. NaShow
instância, como o argumento está disponível, o compilador pode simplesmente expandir o alias do tipo, de modo que a linhaé idêntico em todos os aspectos que o compilador se preocupa com a linha
Em sua
Functor
instância, porém, o compilador não pode expandir o alias de tipo, porque você não forneceu o argumento de tipo. Você poderia imaginar adicionar lambdas de nível de tipo a Haskell, de modo que pudesseinstance Functor Music
significar algo comomas isso tem alguns problemas sérios. A maneira ingênua de adicioná-lo leva à necessidade de fazer uma unificação de ordem superior durante a verificação de tipo, que é indecidível.