La regla básica es que en la programación de FP las funciones 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 decente de PF por ahí le permite tener "variables de instancia" en su función con cierres / alcance léxico.
var make_OO_style_counter = function(){
return {
counter: 0
increment: function(){
this.counter += 1
return this.counter;
}
}
};
var make_FP_style_counter = function(){
var counter = 0;
return fucntion(){
counter += 1
return counter;
}
};
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é usando, así que déjelo para este último. La otra forma de definir una interfaz es la forma estructural, viendo qué parámetros reciben y devuelven. Este es el tipo de interfaz que tiende a ver en lenguajes dinámicos tipográficos y se adapta muy bien a todos los FP: una interfaz son los tipos de parámetros de entrada de 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, evitas la fealdad de pasar las funciones por separado empaquetándolas en algún tipo de registro:
var my_blarfable = {
get_name: function(){ ... },
set_name: function(){ ... },
get_id: function(){ ... }
}
do_something(my_blarfable)
El uso de funciones simples o registros de funciones ayudará en gran medida a 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 ofrecen funciones adicionales. Un ejemplo de las personas mencionadas son las clases de tipo Haskell. Las clases de tipos esencialmente asocian un tipo con uno de esos registros de funciones y le permiten escribir cosas para que los diccionarios sean implícitos y pasen automáticamente a las funciones internas según corresponda.
-- Explicit dictionary version
-- no setters because haskell doesn't like mutable state.
data BlargDict = BlargDict {
blarg_name :: String,
blarg_id :: Integer
}
do_something :: BlargDict -> IO()
do_something blarg_dict = do
print (blarg_name blarg_dict)
print (blarg_id blarg_dict)
-- Typeclass version
class Blargable a where
blag_name :: a -> String
blag_id :: a -> String
do_something :: Blargable a => a -> IO
do_something blarg = do
print (blarg_name blarg)
print (blarg_id blarg)
Sin embargo, una cosa importante a tener en cuenta acerca de las clases de tipos es que los diccionarios están asociados con los tipos, y no con los valores (como lo que ocurre 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 los lleve a los blargables, las clases de tipos limitarán a que todo sea del mismo tipo, mientras que el enfoque del diccionario le permitirá tener blargables de diferentes orígenes (la 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.