Estou tentando comparar as classes de tipo de Haskell e as interfaces de c #. Suponha que exista um Functor
.
Haskell:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Como implementar essa classe de tipo como interface em c #?
O que eu tentei:
interface Functor<A, B>
{
F<B> fmap(Func<A, B> f, F<A> x);
}
Esta é uma implementação inválida e, na verdade, estou preso a um F
tipo genérico que deve ser retornado por fmap
. Como deve ser definido e onde?
É impossível implementar Functor
em C # e por quê? Ou talvez haja outra abordagem?
Respostas:
O sistema de tipos do C # carece de alguns recursos necessários para implementar adequadamente as classes de tipos como uma interface.
Vamos começar com o seu exemplo, mas a chave está mostrando uma conta mais completa do que uma classe de tipo é e faz e, em seguida, tentando mapeá-las para bits C #.
Essa é a definição da classe de tipo ou semelhante à interface. Agora, vamos olhar para uma definição de um tipo e sua implementação dessa classe de tipo.
Agora podemos ver muito obviamente um fato distinto das classes de tipos que você não pode ter com interfaces. A implementação da classe type não faz parte da definição do tipo. No C #, para implementar uma interface, você deve implementá-la como parte da definição do tipo que a implementa. Isso significa que você não pode implementar uma interface para um tipo que você não implementa, mas no Haskell você pode implementar uma classe de tipo para qualquer tipo ao qual tenha acesso.
Essa é provavelmente a maior imediatamente, mas há outra diferença bastante significativa que faz com que o equivalente em C # realmente não funcione tão bem, e você está abordando isso na sua pergunta. É sobre polimorfismo. Além disso, há algumas coisas relativamente genéricas que Haskell permite que você faça com classes de tipo que não traduzem diretamente, especialmente quando você começa a analisar a quantidade de genérico em tipos existenciais ou outras extensões do GHC, como ADTs genéricos.
Veja bem, com Haskell você pode definir os functores
Então, no consumo, você pode ter uma função:
Aqui reside o problema. Em C #, como você escreve essa função?
Portanto, há algumas coisas erradas na versão C #, por um lado, nem tenho certeza de que permitirá que você use o
<b>
qualificador como eu fiz lá, mas sem ele, tenho certeza de que não enviariaShow<>
adequadamente (sinta-se à vontade para tentar e compilar para descobrir; eu não fiz).O maior problema aqui, porém, é que, diferentemente de Haskell, onde tínhamos nossos
Terminal
s definidos como parte do tipo e utilizáveis no lugar do tipo, devido ao C # carecer de polimorfismo paramétrico apropriado (que se torna super óbvio assim que você tenta interoperar F # com C #) você não pode distinguir clara ou claramente se Direita ou Esquerda sãoTerminal
s. O melhor que você pode fazer é usarnull
, mas e se você estiver tentando transformar um valor em um tipoFunctor
ou no caso deEither
distinguir dois tipos que carregam um valor? Agora você precisa usar um tipo e ter dois valores diferentes para verificar e alternar para modelar sua discriminação?A falta de tipos de soma adequados, tipos de união, ADTs, o que você quiser chamá-los realmente diminui muito o que as classes de tipo oferecem, porque no final do dia elas permitem tratar vários tipos (construtores) como um único tipo, e o sistema de tipos subjacentes do .NET simplesmente não tem esse conceito.
fonte
O que você precisa é de duas classes, uma para modelar o genérico de ordem superior (o functor) e outra para modelar o functor combinado com o valor livre A
Portanto, se usarmos a mônada Option (porque todas as mônadas são functores)
Você pode usar métodos de extensão estáticos para converter de IF <Option, B> para Some <A> quando precisar
fonte
pure
na interface do functor genérico: o compilador reclamaIF<Functor, A> pure<A>(A a);
com "O tipoFunctor
não pode ser usado como parâmetro de tipoFunctor
no tipo genérico de métodoIF<Functor, A>
. Não há conversão de boxe ou conversão de parâmetro de tipo deFunctor
paraF<Functor>
". O que isto significa? E por que devemos definirpure
em dois lugares? Além disso, não devepure
ser estático?