Si quiero programar en un estilo "funcional", ¿con qué reemplazaría una interfaz?
interface IFace
{
string Name { get; set; }
int Id { get; }
}
class Foo : IFace { ... }
Tal vez un Tuple<>
?
Tuple<Func<string> /*get_Name*/, Action<String> /*set_Name*/, Func<int> /*get_Id*/> Foo;
La única razón por la que estoy usando una interfaz en primer lugar es porque siempre quiero ciertas propiedades / métodos disponibles.
Editar: algunos detalles más sobre lo que estoy pensando / intentando.
Digamos que tengo un método que tiene tres funciones:
static class Blarf
{
public static void DoSomething(Func<string> getName, Action<string> setName, Func<int> getId);
}
Con una instancia de Bar
puedo usar este método:
class Bar
{
public string GetName();
public void SetName(string value);
public int GetId();
}
...
var bar = new Bar();
Blarf.DoSomething(bar.GetName, bar.SetName, bar.GetId);
Pero eso es un poco doloroso, ya que tengo que mencionar bar
tres veces en una sola llamada. Además, realmente no pretendo que las personas que llaman suministren funciones de diferentes instancias
Blarf.DoSomething(bar1.GetName, bar2.SetName, bar3.GetId); // NO!
En C #, una interface
es una forma de lidiar con esto; pero eso parece un enfoque muy orientado a objetos. Me pregunto si hay una solución más funcional: 1) pasar el grupo de funciones juntas, y 2) asegurarse de que las funciones estén correctamente relacionadas entre sí.
Respuestas:
No trate la programación funcional como una capa delgada sobre la programación imperativa; Hay mucho más que una simple diferencia sintáctica.
En este caso, tiene un
GetID
método que implica la unicidad de los objetos. Este no es un buen enfoque para escribir programas funcionales. Quizás podría decirnos el problema que está tratando de resolver y podríamos darle consejos más significativos.fuente
Haskell y sus derivados tienen clases de tipo que son similares a las interfaces. Aunque parece que está preguntando sobre cómo hacer la encapsulación, que es una pregunta con respecto a los sistemas de tipos. El sistema de tipos de Milner hindley es común en los lenguajes funcionales, y tiene tipos de datos que hacen esto por usted de diferentes maneras según los idiomas.
fuente
Hay algunas formas de permitir que una función maneje múltiples entradas.
Primero y más común: polimorfismo paramétrico.
Esto permite que una función actúe sobre tipos arbitrarios:
Lo cual es bueno, pero no le da el despacho dinámico que tienen las interfaces OO. Para esto Haskell tiene clases de tipos, Scala tiene implicits, etc.
Entre estos dos mecanismos, puede expresar todo tipo de comportamientos complejos e interesantes en sus tipos.
fuente
La regla general básica es que en las funciones de programación FP hacen el mismo trabajo que los objetos en la programación OO. Puede llamar a sus métodos (bueno, el método de "llamada" de todos modos) y responden de acuerdo con algunas reglas internas encapsuladas. En particular, cada lenguaje FP decente le permite tener "variables de instancia" en su función con cierres / alcance léxico.
Ahora la siguiente pregunta es, ¿qué quieres decir con una interfaz? Un enfoque es el uso de interfaces nominales (se ajusta a la interfaz si así lo dice); este generalmente depende mucho del idioma que esté utilizando, así que vamos a dejarlo para este último. La otra forma de definir una interfaz es la forma estructural, ver qué parámetros reciben y devuelven. Este es el tipo de interfaz que tiende a ver en lenguajes dinámicos de tipo pato y se adapta muy bien a todos los FP: una interfaz son solo los tipos de parámetros de entrada para nuestras funciones y los tipos que devuelven, por lo que todas las funciones coinciden con los tipos correctos se ajustan a la interfaz!
Por lo tanto, la forma más sencilla de representar un objeto que coincida con una interfaz es simplemente tener un grupo de funciones. Por lo general, evita la fealdad de pasar las funciones por separado al empaquetarlas en algún tipo de registro:
El uso de funciones desnudas o registros de funciones será de gran ayuda para resolver la mayoría de sus problemas comunes de una manera "libre de grasa" sin toneladas de repetitivo. Si necesita algo más avanzado que eso, a veces los idiomas le brindan funciones adicionales. Un ejemplo que la gente mencionó son las clases de tipo Haskell. Las clases de tipos esencialmente asocian un tipo con uno de esos registros de funciones y le permite escribir cosas para que los diccionarios sean implícitos y pasen automáticamente a las funciones internas según corresponda.
Sin embargo, una cosa importante a tener en cuenta sobre las clases de tipos es que los diccionarios están asociados con los tipos, y no con los valores (como lo que sucede en el diccionario y las versiones OO). Esto significa que el sistema de tipos no le permite mezclar "tipos" [1]. Si desea una lista de "blargables" o una función binaria que lleve a blargables, las clases de tipos restringirán que todo sea del mismo tipo, mientras que el enfoque del diccionario le permitirá tener blargables de diferentes orígenes (qué versión es mejor depende mucho de lo que sea) haciendo)
[1] Hay formas avanzadas de hacer "tipos existenciales" pero generalmente no vale la pena.
fuente
Creo que va a ser específico del idioma. Vengo de un fondo lispy. En muchos casos, las interfaces con estado rompen el modelo funcional hasta cierto punto. Entonces CLOS, por ejemplo, es donde LISP es menos funcional y más cercano a un lenguaje imperativo. En general, los parámetros de función requeridos combinados con métodos de nivel superior son probablemente lo que está buscando.
fuente