10 votes

En Scala, comment combiner la programmation événementielle avec une approche fonctionnelle ?

Pour clarifier ce que j'entends par événement, je fais référence à une situation où j'ai

def onTrade(...)

Qui est appelé chaque fois qu'une action particulière est négociée. Supposons que je veuille suivre le cours le plus élevé de la journée. Pour moi, la solution évidente est :

var dailyHigh = 0

def onTrade(...) {
    if (price > dailyHigh) dailyHigh = price
}

Existe-t-il un moyen de réaliser cette fonctionnalité en utilisant val au lieu de var ? Je suppose également que je pourrais vouloir ajouter dailyLow, volumeHigh, volumeLow etc. à l'avenir.

9voto

Didier Dupont Points 18256

Le document Dépréciation du modèle de l'observateur pourrait être intéressant, mais je crois que la bibliothèque qu'il décrit n'est pas encore disponible.

8voto

Daniel C. Sobral Points 159554

Pas vraiment un problème, en fait. Une solution complète utiliserait probablement les monades Reader, IO et State, plus Iteratee et les objectifs, mais voici une version plus simple :

case class State(dailyHigh: Int = 0)

object Main {
  type Event = (State => State)

  def mainLoop(currState: State, events: Stream[Event]): State =
    if (events.nonEmpty) {
      val newState = events.head(currState)
      mainLoop(newState, events.tail)
    } else currState

  def onTrade(price: Int): Event = (s: State) =>
    if (price > s.dailyHigh) s.copy(dailyHigh = price) else s

  def main(args: Array[String]) {
    val events = onTrade(5) #:: onTrade(2) #:: onTrade(10) #:: onTrade(5) #:: Stream.empty
    val finalState = mainLoop(State(), events)
    println(finalState)
  }
}

Regarde, maman, pas de vars !

L'État peut devenir assez complexe, bien sûr, mais c'est là que les objectifs entrent en jeu. Avec les lentilles, il est assez facile de consulter et de modifier (copier avec une nouvelle valeur) des structures de données arbitrairement complexes.

L'utilisation d'itérations est naturelle pour les événements -- dans un sens très simple, "onTrade" devient une itération qui est invoquée par un énumérateur (la chose qui "génère" les événements) avec chaque événement si elle est composée à partir d'une fonction partielle, vous pouvez toutes les plier en une seule fonction partielle.

Une autre solution consiste à combiner les monades d'état avec les monades d'entrée/sortie sur les for-compréhensions.

Enfin, il y a l'option des continuations. Si un traitement nécessite la réception d'une chaîne d'événements, alors le résultat de chaque événement peut être une continuation, et la continuation elle-même devient une partie de l'état.

2voto

jilen Points 1591

Parfois, le statut mutable est naturellement nécessaire. Voici un exemple tiré du livre 'scala by example'.
Il a aussi quelques mutable status(maxBid,maxBidder) 。So a var n'est pas toujours une mauvaise idée. Parfois, cela fonctionne bien.

   class Auction(seller: Actor, minBid: Int, closing: Date) extends Actor {
   val timeToShutdown = 36000000 // msec
   val bidIncrement = 10
   def act() {
      var maxBid = minBid - bidIncrement
      var maxBidder: Actor = null
      var running = true
      while (running) {
         receiveWithin((closing.getTime() - new Date().getTime())) {
            case Offer(bid, client) =>
               if (bid >= maxBid + bidIncrement) {
                  if (maxBid >= minBid) maxBidder ! BeatenOffer(bid)
                  maxBid = bid; maxBidder = client; client ! BestOffer
               } else {
                  client ! BeatenOffer(maxBid)
               }
            case Inquire(client) =>
               client ! Status(maxBid, closing)
            case TIMEOUT =>
               if (maxBid >= minBid) {
                  val reply = AuctionConcluded(seller, maxBidder)
                  maxBidder ! reply; seller ! reply
               } else {
                  seller ! AuctionFailed
               }
               receiveWithin(timeToShutdown) {
                  case Offer(_, client) => client ! AuctionOver
                  case TIMEOUT          => running = false
               }
         }
      }
   }
}

0voto

Jens Schauder Points 23468

Je ne l'ai jamais fait, mais au lieu de modifier les valeurs, vous pourriez créer de nouvelles instances dans un flux.

D'autres processus pourraient alors itérer ce flux, ce qui les ferait attendre lorsqu'ils atteignent le dernier élément instancié d'un flux.

0voto

AndreasScheinert Points 1387

Je recommande vivement la programmation fonctionnelle réactive pour cette tâche. Voici un exposé sur une telle bibliothèque en scala : http://skillsmatter.com/podcast/scala/reactors

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X