Question Comment une fonction temporelle peut-elle exister dans la programmation fonctionnelle?


Je dois admettre que je ne connais pas grand-chose à la programmation fonctionnelle. Je l'ai lu d'ici et de là, et j'ai ainsi appris que dans la programmation fonctionnelle, une fonction renvoie la même sortie, pour la même entrée, peu importe le nombre de fois que la fonction est appelée. C'est exactement comme une fonction mathématique qui évalue à la même sortie pour la même valeur des paramètres d'entrée qui implique dans l'expression de la fonction.

Par exemple, considérez ceci:

f(x,y) = x*x + y; // It is a mathematical function

Peu importe combien de fois vous utilisez f(10,4), sa valeur sera toujours 104. En tant que tel, partout où vous avez écrit f(10,4), vous pouvez le remplacer par 104, sans altérer la valeur de l'expression entière. Cette propriété est appelée transparence référentielle d'une expression.

Comme dit Wikipedia (lien),

Inversement, dans le code fonctionnel, la valeur de sortie d'une fonction dépend uniquement des arguments qui sont entrés dans la fonction, donc appeler une fonction f deux fois avec la même valeur pour un argument x produira le même résultat f (x) les deux fois.

Peut une fonction de temps (qui renvoie le actuel temps) existe-t-il dans la programmation fonctionnelle?

  • Si oui, alors comment peut-il exister? Cela ne viole-t-il pas le principe de la programmation fonctionnelle? Cela viole particulièrement transparence référentielle qui est l'une des propriétés de la programmation fonctionnelle (si je la comprends correctement).

  • Ou si non, alors comment peut-on connaître l'heure actuelle dans la programmation fonctionnelle?


585
2017-09-01 08:26


origine


Réponses:


Une autre façon de l'expliquer est la suivante: non fonction peut obtenir l'heure actuelle (car il ne cesse de changer), mais un action peut obtenir l'heure actuelle. Disons que getClockTime est une constante (ou une fonction nulle, si vous voulez) qui représente le action d'avoir l'heure actuelle. Ce action est le même à chaque fois, peu importe quand il est utilisé, donc c'est une vraie constante.

De même, disons print est une fonction qui prend une certaine représentation temporelle et l'imprime sur la console. Puisque les appels de fonction ne peuvent pas avoir d'effets secondaires dans un langage fonctionnel pur, nous imaginons plutôt que c'est une fonction qui prend un horodatage et retourne le action de l'imprimer sur la console. Encore une fois, c'est une fonction réelle, car si vous lui donnez le même horodatage, il retournera le même action de l'imprimer à chaque fois.

Maintenant, comment pouvez-vous imprimer l'heure actuelle sur la console? Eh bien, vous devez combiner les deux actions. Alors, comment pouvons-nous faire cela? Nous ne pouvons pas simplement passer getClockTime à print, car l'impression attend un horodatage, pas une action. Mais on peut imaginer qu'il y a un opérateur, >>=, lequel combine deux actions, une qui obtient un horodatage, et une qui prend un comme argument et l'imprime. En appliquant ceci aux actions mentionnées précédemment, le résultat est ... tadaaa ... une nouvelle action qui obtient l'heure actuelle et l'imprime. Et c'est d'ailleurs exactement ce qui se passe à Haskell.

Prelude> System.Time.getClockTime >>= print
Fri Sep  2 01:13:23 東京 (標準時) 2011

Donc, conceptuellement, vous pouvez le voir de la façon suivante: Un programme purement fonctionnel n'effectue aucune E / S, il définit un action, que le système d'exécution exécute ensuite. le action est le même à chaque fois, mais le résultat de son exécution dépend des circonstances de son exécution.

Je ne sais pas si c'était plus clair que les autres explications, mais cela m'aide parfois à penser de cette façon.


128
2017-09-01 16:28



Oui et non.

Différents langages de programmation fonctionnels les résolvent différemment.

Dans Haskell (un très pur) tout ce truc doit arriver dans quelque chose appelé le I / O Monad - voir ici.

Vous pouvez penser à cela comme obtenir une autre entrée (et sortie) dans votre fonction (l'état du monde) ou plus facilement comme un lieu où «l'impureté», comme l'arrivée du temps qui change, se produit.

D'autres langages comme F # ont juste une certaine impureté et donc vous pouvez avoir une fonction qui retourne des valeurs différentes pour la même entrée - tout comme Ordinaire langues impératives.

Comme Jeffrey Burka l'a mentionné dans son commentaire: Voici la belle introduction à la Monad I / O directement depuis le wiki Haskell.


342
2017-09-01 08:31



Dans Haskell on utilise une construction appelée monade pour gérer les effets secondaires. Une monade signifie essentiellement que vous encapsulez des valeurs dans un conteneur et avez des fonctions pour chaîner des fonctions de valeurs à des valeurs à l'intérieur d'un conteneur. Si notre conteneur a le type:

data IO a = IO (RealWorld -> (a,RealWorld))

nous pouvons mettre en œuvre en toute sécurité les actions d'E / S. Ce type signifie: Une action de type IO est une fonction, qui prend un jeton de type RealWorld et renvoie un nouveau jeton, avec un résultat.

L'idée derrière cela est que chaque action d'E / S fait muter l'état extérieur, représenté par le jeton magique RealWorld. En utilisant des monades, on peut enchaîner plusieurs fonctions qui mutent le monde réel ensemble. La fonction la plus importante d'une monade est >>=, prononcé lier:

(>>=) :: IO a -> (a -> IO b) -> IO b

>>= prend une action et une fonction qui prend le résultat de cette action et crée une nouvelle action à partir de cela. Le type de retour est la nouvelle action. Par exemple, supposons qu'il y a une fonction now :: IO String, qui renvoie une chaîne représentant l'heure actuelle. Nous pouvons l'enchaîner avec la fonction putStrLn pour l'imprimer:

now >>= putStrLn

Ou écrit dans do-Notation, qui est plus familier à un programmeur impératif:

do currTime <- now
   putStrLn currTime

Tout cela est pur, comme nous cartographions la mutation et l'information sur le monde extérieur à la RealWorld jeton. Donc, à chaque fois, vous exécutez cette action, vous obtenez bien sûr une sortie différente, mais l'entrée n'est pas la même: le RealWorld jeton est différent.


136
2017-09-01 09:18



La plupart des langages de programmation fonctionnels ne sont pas purs, c'est-à-dire qu'ils permettent aux fonctions de ne pas dépendre uniquement de leurs valeurs. Dans ces langues, il est parfaitement possible d'avoir une fonction renvoyant l'heure actuelle. Parmi les langues que vous avez taguées cette question s'applique à Scala et F# (ainsi que la plupart des autres variantes de ML).

Dans des langues comme Haskell et Nettoyer, qui sont purs, la situation est différente. Dans Haskell, l'heure actuelle ne serait pas disponible à travers une fonction, mais une action appelée IO, qui est la manière d'encapsuler les effets secondaires de Haskell.

Dans Clean, il s'agirait d'une fonction, mais la fonction prendrait une valeur mondiale comme argument et retournerait une nouvelle valeur mondiale (en plus de l'heure actuelle) comme résultat. Le système de types ferait en sorte que chaque valeur mondiale ne puisse être utilisée qu'une seule fois (et chaque fonction qui consomme une valeur mondiale en produirait une nouvelle). De cette façon, la fonction temporelle devrait être appelée avec un argument différent à chaque fois et ainsi être autorisée à retourner une heure différente à chaque fois.


65
2017-09-01 08:36



"Heure actuelle" n'est pas une fonction. C'est un paramètre. Si votre code dépend de l'heure actuelle, cela signifie que votre code est paramétré par heure.


44
2017-09-01 17:46



Cela peut absolument être fait de manière purement fonctionnelle. Il y a plusieurs façons de le faire, mais le plus simple est de faire en sorte que la fonction temps ne retourne pas seulement le temps mais aussi la fonction que vous devez appeler pour obtenir la prochaine mesure de temps.

En C #, vous pouvez l'implémenter comme ceci:

// Exposes mutable time as immutable time (poorly, to illustrate by example)
// Although the insides are mutable, the exposed surface is immutable.
public class ClockStamp {
    public static readonly ClockStamp ProgramStartTime = new ClockStamp();
    public readonly DateTime Time;
    private ClockStamp _next;

    private ClockStamp() {
        this.Time = DateTime.Now;
    }
    public ClockStamp NextMeasurement() {
        if (this._next == null) this._next = new ClockStamp();
        return this._next;
    }
}

(Gardez à l'esprit que cet exemple est destiné à être simple, pas pratique.En particulier, les nœuds de liste ne peuvent pas être collectés car ils sont rootés par ProgramStartTime.)

Cette classe 'ClockStamp' agit comme une liste liée immuable, mais en réalité les noeuds sont générés à la demande afin qu'ils puissent contenir l'heure 'courante'. Toute fonction qui souhaite mesurer l'heure doit avoir un paramètre 'clockStamp' et doit également renvoyer sa dernière mesure de temps dans son résultat (afin que l'appelant ne voit pas les anciennes mesures), comme ceci:

// Immutable. A result accompanied by a clockstamp
public struct TimeStampedValue<T> {
    public readonly ClockStamp Time;
    public readonly T Value;
    public TimeStampedValue(ClockStamp time, T value) {
        this.Time = time;
        this.Value = value;
    }
}

// Times an empty loop.
public static TimeStampedValue<TimeSpan> TimeALoop(ClockStamp lastMeasurement) {
    var start = lastMeasurement.NextMeasurement();
    for (var i = 0; i < 10000000; i++) {
    }
    var end = start.NextMeasurement();
    var duration = end.Time - start.Time;
    return new TimeStampedValue<TimeSpan>(end, duration);
}

public static void Main(String[] args) {
    var clock = ClockStamp.ProgramStartTime;
    var r = TimeALoop(clock);
    var duration = r.Value; //the result
    clock = r.Time; //must now use returned clock, to avoid seeing old measurements
}

Bien sûr, c'est un peu gênant d'avoir à passer cette dernière mesure, dedans et dehors, dedans et dehors. Il y a plusieurs façons de masquer le passe-partout, en particulier au niveau de la conception de la langue. Je pense que Haskell utilise ce genre de truc, puis cache les parties laides en utilisant des monades.


20
2018-06-13 13:33



Je suis surpris qu'aucune des réponses ou des commentaires ne mentionne de coalgebras ou de coinduction. Habituellement, la co-induction est mentionnée lorsque l'on raisonne sur des structures de données infinies, mais elle est également applicable à un flux infini d'observations, tel qu'un registre de temps sur une CPU. Un coalgebra modélise l'état caché; et modèles de coinduction observer cet état. (Modèles à induction normale construire Etat.)

C'est un sujet brûlant dans la programmation fonctionnelle réactive. Si vous êtes intéressé par ce genre de choses, lisez ceci: http://digitalcommons.ohsu.edu/csetech/91/ (28 pp.)


13
2017-09-19 00:11



Oui, il est possible qu'une fonction pure renvoie l'heure, si elle est paramétrée. Argument de temps différent, résultat de temps différent. Ensuite, formez également d'autres fonctions du temps et combinez-les avec un vocabulaire simple de fonctions (-of-time) -transforming (d'ordre supérieur). Puisque l'approche est sans état, le temps ici peut être continu (indépendant de la résolution) plutôt que discret, grandement stimuler la modularité. Cette intuition est la base de la programmation réactive fonctionnelle (PRF).


11
2018-02-25 17:20