Estoy tratando de comparar las clases de tipos de Haskell y las interfaces de C #. Supongamos que hay un Functor
.
Haskell
class Functor f where
fmap :: (a -> b) -> f a -> f b
¿Cómo implementar esta clase de tipo como interfaz en C #?
Lo que he intentado:
interface Functor<A, B>
{
F<B> fmap(Func<A, B> f, F<A> x);
}
Esta es una implementación no válida y en realidad estoy atrapado con un F
tipo genérico que debería devolver fmap
. ¿Cómo debe definirse y dónde?
¿Es imposible implementar Functor
en C # y por qué? ¿O tal vez hay otro enfoque?
Respuestas:
El sistema de tipos de C # carece de un par de características necesarias para implementar correctamente las clases de tipos como una interfaz.
Comencemos con su ejemplo, pero la clave es mostrar una cuenta más completa de lo que es y hace una clase de tipos, y luego tratar de asignarlos a bits C #.
Esta es la definición de clase de tipo, o similar a la interfaz. Ahora veamos una definición de un tipo y su implementación de esa clase de tipo.
Ahora podemos ver muy obviamente un hecho distinto de las clases de tipos que no puede tener con las interfaces. La implementación de la clase de tipo no forma parte de la definición del tipo. En C #, para implementar una interfaz, debe implementarla como parte de la definición del tipo que la implementa. Esto significa que no puede implementar una interfaz para un tipo que no implementa usted mismo, sin embargo, en Haskell puede implementar una clase de tipo para cualquier tipo al que tenga acceso.
Esa es probablemente la más grande de inmediato, pero hay otra diferencia bastante significativa que hace que el equivalente de C # realmente no funcione tan bien, y lo está abordando en su pregunta. Se trata de polimorfismo. También hay algunas cosas relativamente genéricas que Haskell le permite hacer con clases de tipos que no se traducen directamente, especialmente cuando comienza a observar la cantidad de generismo en los tipos existenciales u otras extensiones de GHC como ADT genéricos.
Verás, con Haskell puedes definir los functores
Luego, en el consumo puede tener una función:
Aquí yace el problema. En C #, ¿cómo se escribe esta función?
Entonces, hay un par de cosas mal con la versión C #, por una cosa, ni siquiera estoy seguro de que te permita usar el
<b>
calificador como lo hice allí, pero sin él, estoy seguro de que no se despacharáShow<>
adecuadamente (siéntete libre de intentarlo y compilar para averiguarlo; no lo hice).Sin embargo, el problema más grande aquí es que, a diferencia de lo anterior en Haskell, donde definimos nuestros
Terminal
s como parte del tipo y luego los usamos en lugar del tipo, debido a que C # carece del polimorfismo paramétrico apropiado (que se vuelve súper obvio tan pronto como intentas interoperar) F # con C #) no puede distinguir clara o limpiamente si Derecha o Izquierda sonTerminal
s. Lo mejor que puede hacer es usarnull
, pero ¿qué sucede si está tratando de hacer un tipo de valor aoFunctor
en el caso deEither
que distinga dos tipos que tienen un valor? ¿Ahora tiene que usar un tipo y tener dos valores diferentes para verificar y cambiar para modelar su discriminación?La falta de tipos de suma adecuados, tipos de unión, ADT, como quiera llamarlos realmente hace mucho de lo que las clases de tipos le dan, porque al final del día le permiten tratar múltiples tipos (constructores) como un solo tipo, y el sistema de tipos subyacente de .NET simplemente no tiene ese concepto.
fuente
Lo que necesita son dos clases, una para modelar el genérico de orden superior (el functor) y otra para modelar el functor combinado con el valor libre A
Entonces, si usamos la opción mónada (porque todas las mónadas son functores)
Luego puede usar métodos de extensión estática para convertir de IF <Opción, B> a Algunos <A> cuando necesite
fuente
pure
la interfaz genérica de functor: el compilador se quejaIF<Functor, A> pure<A>(A a);
con "El tipoFunctor
no se puede usar como parámetro de tipoFunctor
en el tipo de método genéricoIF<Functor, A>
. No hay conversión de boxeo o conversión de parámetro de tipo deFunctor
aF<Functor>
". ¿Qué significa esto? ¿Y por qué debemos definirpure
en dos lugares? Además, ¿no deberíapure
ser estático?