Advertencia justa, soy nuevo en la programación funcional, por lo que puedo tener muchos supuestos erróneos.
He estado aprendiendo sobre tipos algebraicos. Muchos lenguajes funcionales parecen tenerlos, y son bastante útiles junto con la coincidencia de patrones. Sin embargo, ¿qué problema resuelven realmente? Puedo implementar un tipo algebraico aparentemente (más o menos) en C # como este:
public abstract class Option { }
public class None : Option { }
public class Some<T> : Option
{
public T Value { get; set; }
}
var result = GetSomeValue();
if(result is None)
{
}
else
{
}
Pero creo que la mayoría estaría de acuerdo en que esto es una bastarización de la programación orientada a objetos, y nunca deberías hacerlo. Entonces, ¿la programación funcional solo agrega una sintaxis más limpia que hace que este estilo de programación parezca menos asqueroso? ¿Qué más me estoy perdiendo?
functional-programming
algebraic-data-type
CondiciónRacer
fuente
fuente
class ThirdOption : Option{}
y le doy un lugarnew ThirdOption()
donde esperabaSome
oNone
?data Maybe a = Just a | Nothing
(equivalente adata Option a = Some a | None
en su ejemplo): no puede agregar un tercer caso post-hoc. Si bien puedes emular tipos de suma en C # de la manera que has mostrado, no es el más bonito.Respuestas:
Las clases con interfaces y herencia presentan un mundo abierto: cualquiera puede agregar un nuevo tipo de datos. Para una interfaz determinada, puede haber clases que la implementen en todo el mundo, en diferentes archivos, en diferentes proyectos, en diferentes compañías. Facilitan agregar casos a las estructuras de datos, pero debido a que las implementaciones de la interfaz están descentralizadas, es difícil agregar un nuevo método a la interfaz. Una vez que una interfaz es pública, se congela básicamente. Nadie conoce todas las implementaciones posibles.
Los tipos de datos algebraicos son duales a eso, están cerrados . Todos los casos de los datos se enumeran en un solo lugar y las operaciones no solo pueden enumerar las variantes de forma exhaustiva, sino que se les recomienda que lo hagan. Consecuentemente, escribir una nueva función que opera en un tipo de datos algebraicos es trivial: solo escriba la maldita función. A cambio, agregar nuevos casos es complicado porque necesita revisar básicamente toda la base de código y extender cada uno
match
. Similar a la situación con las interfaces, en la biblioteca estándar Rust, agregar una nueva variante es un cambio radical (para tipos públicos).Estos son dos lados del problema de expresión . Los tipos de datos algebraicos son una solución incompleta para ellos, pero también lo es la POO. Ambos tienen ventajas dependiendo de cuántos casos de datos hay, con qué frecuencia cambian esos casos y con qué frecuencia se amplían o cambian las operaciones. (Es por eso que muchos lenguajes modernos proporcionan ambos, o algo similar, o van directamente a mecanismos más potentes y más complicados que intentan subsumir ambos enfoques).
fuente
Esa es una simplificación quizás, pero sí.
Seamos claros sobre qué tipos de datos algebraicos son (resumiendo este excelente enlace de Aprenda usted como Haskell):
tu ejemplo solo funciona realmente con el primero.
Quizás lo que se está perdiendo es que al proporcionar estas dos operaciones básicas, los lenguajes funcionales le permiten construir todo lo demás. C # tiene estructuras, clases, enumeraciones, genéricos además de esos y montones de reglas para gobernar cómo se comportan estas cosas.
Combinado con alguna sintaxis para ayudar, los lenguajes funcionales pueden descomponer las operaciones en estos dos caminos, proporcionando un enfoque limpio, simple y elegante para los tipos.
Resuelven el mismo problema que cualquier otro tipo de sistema: "¿qué valores son legales para usar aquí?" - Simplemente toman un enfoque diferente.
fuente
Puede sorprenderle saber que la coincidencia de patrones no se considera la forma más idiomática de trabajar con Opciones. Consulte la documentación de las Opciones de Scala para obtener más información al respecto. No estoy seguro de por qué tantos tutoriales de FP fomentan este uso.
Principalmente, lo que falta es que hay un montón de funciones creadas para facilitar el trabajo con las Opciones. Considere el ejemplo principal de los documentos de Scala:
Observe cómo
map
y lefilter
permite encadenar operaciones en Opciones, sin tener que verificar en cada punto si tieneNone
o no. Luego, al final, se utilizagetOrElse
para especificar un valor predeterminado. En ningún momento estás haciendo algo "asqueroso" como verificar tipos. Cualquier verificación de tipo inevitable se realiza internamente en la biblioteca. Haskell tiene su propio conjunto de funciones análogas, sin mencionar el gran conjunto de funciones que funcionará en cualquier mónada o functor.Otros tipos de datos algebraicos tienen sus propias formas idiomáticas de trabajar con ellos, y la coincidencia de patrones es baja en el tótem en la mayoría de los casos. Del mismo modo, cuando crea sus propios tipos, se espera que proporcione funciones similares para trabajar con ellos.
fuente