Si je veux programmer dans un style "fonctionnel", avec quoi remplacer une interface?
interface IFace
{
string Name { get; set; }
int Id { get; }
}
class Foo : IFace { ... }
Peut-être un Tuple<>
?
Tuple<Func<string> /*get_Name*/, Action<String> /*set_Name*/, Func<int> /*get_Id*/> Foo;
La seule raison pour laquelle j'utilise une interface en premier lieu est parce que je veux toujours que certaines propriétés / méthodes soient disponibles.
Edit: Plus de détails sur ce que je pense / tente.
Disons que j'ai une méthode qui prend trois fonctions:
static class Blarf
{
public static void DoSomething(Func<string> getName, Action<string> setName, Func<int> getId);
}
Avec une instance de Bar
je peux utiliser cette méthode:
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);
Mais c'est un peu pénible car je dois le mentionner bar
trois fois en un seul appel. De plus, je n'ai pas vraiment l'intention que les appelants fournissent des fonctions à partir de différentes instances
Blarf.DoSomething(bar1.GetName, bar2.SetName, bar3.GetId); // NO!
En C #, an interface
est une façon de gérer cela; mais cela semble être une approche très orientée objet. Je me demande s'il existe une solution plus fonctionnelle: 1) passer le groupe de fonctions ensemble, et 2) s'assurer que les fonctions sont correctement liées les unes aux autres.
Réponses:
Ne traitez pas la programmation fonctionnelle comme un placage fin par rapport à la programmation impérative; il y a bien plus qu'une simple différence syntaxique.
Dans ce cas, vous avez une
GetID
méthode, ce qui implique l'unicité des objets. Ce n'est pas une bonne approche pour écrire des programmes fonctionnels. Vous pourriez peut-être nous dire le problème que vous essayez de résoudre et nous pourrions vous donner des conseils plus significatifs.la source
Haskell et ses dérivés ont des classes de types similaires aux interfaces. Bien qu'il semble que vous vous demandiez comment effectuer l'encapsulation, c'est une question concernant les systèmes de types. Le système de type Hindley Milner est courant dans les langages fonctionnels, et il a des types de données qui le font pour vous de manière variable selon les langues.
la source
Il existe plusieurs façons de permettre à une fonction de gérer plusieurs entrées.
Premier et plus courant: le polymorphisme paramétrique.
Cela permet à une fonction d'agir sur des types arbitraires:
Ce qui est bien, mais ne vous donne pas la répartition dynamique des interfaces OO. Pour cela, Haskell a des classes de caractères, Scala a des implications, etc.
Entre ces deux mécanismes, vous pouvez exprimer toutes sortes de comportements complexes et intéressants sur vos types.
la source
La règle de base est que dans la programmation FP, les fonctions font le même travail que les objets dans la programmation OO. Vous pouvez appeler leurs méthodes (enfin, la méthode "call" de toute façon) et ils répondent selon certaines règles internes encapsulées. En particulier, chaque langage FP décent vous permet d'avoir des "variables d'instance" dans votre fonction avec des fermetures / portée lexicale.
Maintenant, la question suivante est ce que vous entendez par une interface? Une approche consiste à utiliser des interfaces nominales (elle se conforme à l'interface si elle le dit) - celle-ci dépend généralement beaucoup de la langue que vous utilisez, alors laissons-la pour cette dernière. L'autre façon de définir une interface est la manière structurelle, de voir quels paramètres la chose reçoit et retourne. C'est le type d'interface que vous avez tendance à voir dans les langages dynamiques de type canard et il correspond très bien à tous les FP: une interface est juste les types des paramètres d'entrée de nos fonctions et les types qu'ils renvoient donc Toutes les fonctions correspondant à la les bons types correspondent à l'interface!
Par conséquent, la façon la plus simple de représenter un objet correspondant à une interface est simplement d'avoir un groupe de fonctions. Vous contournez généralement la laideur de passer les fonctions séparément en les emballant dans une sorte d'enregistrement:
L'utilisation de fonctions nues ou d'enregistrements de fonctions contribuera grandement à résoudre la plupart de vos problèmes courants de manière "sans gras" sans tonnes de passe-partout. Si vous avez besoin de quelque chose de plus avancé que cela, parfois les langues vous offrent des fonctionnalités supplémentaires. Un exemple que les gens ont mentionné est celui des classes de type Haskell. Les classes de types associent essentiellement un type à l'un de ces enregistrements de fonctions et vous permettent d'écrire des choses afin que les dictionnaires soient implicites et soient automatiquement transmis aux fonctions internes selon le cas.
Cependant, une chose importante à noter à propos des classes de types est que les dictionnaires sont associés aux types et non aux valeurs (comme ce qui se passe dans les versions dictionnaire et OO). Cela signifie que le système de type ne vous permet pas de mélanger des "types" [1]. Si vous voulez une liste de "blargables" ou une fonction binaire prenant les blargables, alors les classes de caractères contraindront tout à être du même type tandis que l'approche par dictionnaire vous permettra d'avoir des blargables d'origines différentes (quelle version est la meilleure dépend beaucoup de ce que vous êtes) Faire)
[1] Il existe des moyens avancés de faire des "types existentiels" mais cela ne vaut généralement pas la peine.
la source
Je pense que ça va être spécifique à la langue. Je viens d'un fond vif. Dans de nombreux cas, les interfaces avec l'état cassent le modèle fonctionnel dans une certaine mesure. Ainsi, CLOS, par exemple, est l'endroit où LISP est moins fonctionnel et plus proche d'un langage impératif. Généralement, les paramètres de fonction requis combinés avec des méthodes de niveau supérieur sont probablement ce que vous recherchez.
la source