76 votes

Scalaz état de monade exemples

Je n'ai pas vu beaucoup d'exemples de scalaz état de monade. Il y a cet exemple , mais c'est difficile à comprendre et il y a seulement une autre question sur un débordement de pile, il me semble.

Je vais poster quelques exemples que j'ai joué avec, mais j'aimerais avoir d'autres. Aussi, si quelqu'un peut fournir exemple sur pourquoi est - init, modify, put et gets sont utilisés pour ce serait génial.

Edit: ici est un jeu génial de 2 heures de présentation sur l'état de monade.

83voto

huynhjl Points 26045

Je suppose, scalaz 7.0.x et les importations suivantes (voir la réponse de l'histoire de scalaz 6.x):

import scalaz._
import Scalaz._

L'état type est défini comme State[S, A]S est le type de l'état et de l' A est le type de la valeur décoré. La syntaxe de base pour créer une valeur de l'état de fait de l'utilisation de l' State[S, A] fonction de:

// Create a state computation incrementing the state and returning the "str" value
val s = State[Int, String](i => (i + 1, "str")) 

Pour exécuter l'état de calcul sur une valeur initiale:

// start with state of 1, pass it to s
s.eval(1)
// returns result value "str"

// same but only retrieve the state
s.exec(1)
// 2

// get both state and value
s(1) // or s.run(1)
// (2, "str")

L'état peut être enfilé à travers les appels de fonction. Pour ce faire, au lieu de Function[A, B], définissent Function[A, State[S, B]]]. Utiliser l' State de la fonction...

import java.util.Random
def dice() = State[Random, Int](r => (r, r.nextInt(6) + 1))

Puis l' for/yield de la syntaxe peuvent être utilisés pour composer les fonctions de:

def TwoDice() = for {
  r1 <- dice()
  r2 <- dice()
} yield (r1, r2)

// start with a known seed 
TwoDice().eval(new Random(1L))
// resulting value is (Int, Int) = (4,5)

Voici un autre exemple. Remplir une liste avec TwoDice() état des calculs.

val list = List.fill(10)(TwoDice())
// List[scalaz.IndexedStateT[scalaz.Id.Id,Random,Random,(Int, Int)]]

Utilisation de la séquence pour obtenir un State[Random, List[(Int,Int)]]. Nous pouvons fournir un alias de type.

type StateRandom[x] = State[Random,x]
val list2 = list.sequence[StateRandom, (Int,Int)]
// list2: StateRandom[List[(Int, Int)]] = ...
// run this computation starting with state new Random(1L)
val tenDoubleThrows2 = list2.eval(new Random(1L))
// tenDoubleThrows2  : scalaz.Id.Id[List[(Int, Int)]] =
//   List((4,5), (2,4), (3,5), (3,5), (5,5), (2,2), (2,4), (1,5), (3,1), (1,6))

Ou nous pouvons utiliser sequenceU qui permettra d'en déduire les types:

val list3 = list.sequenceU
val tenDoubleThrows3 = list3.eval(new Random(1L))
// tenDoubleThrows3  : scalaz.Id.Id[List[(Int, Int)]] = 
//   List((4,5), (2,4), (3,5), (3,5), (5,5), (2,2), (2,4), (1,5), (3,1), (1,6))

Un autre exemple, State[Map[Int, Int], Int] pour calculer la fréquence de sommes sur la liste ci-dessus. freqSum calcule la somme de la lance et compte fréquences.

def freqSum(dice: (Int, Int)) = State[Map[Int,Int], Int]{ freq =>
  val s = dice._1 + dice._2
  val tuple = s -> (freq.getOrElse(s, 0) + 1)
  (freq + tuple, s)
}

Maintenant, utilisez parcourir pour appliquer freqSum sur tenDoubleThrows. traverse est équivalent à map(freqSum).sequence.

type StateFreq[x] = State[Map[Int,Int],x]
// only get the state
tenDoubleThrows2.copoint.traverse[StateFreq, Int](freqSum).exec(Map[Int,Int]())
// Map(10 -> 1, 6 -> 3, 9 -> 1, 7 -> 1, 8 -> 2, 4 -> 2) : scalaz.Id.Id[Map[Int,Int]]

Ou de façon plus succincte en utilisant traverseU d'inférer les types:

tenDoubleThrows2.copoint.traverseU(freqSum).exec(Map[Int,Int]())
// Map(10 -> 1, 6 -> 3, 9 -> 1, 7 -> 1, 8 -> 2, 4 -> 2) : scalaz.Id.Id[Map[Int,Int]]

Notez que parce qu' State[S, A] est un alias de type pour StateT[Id, S, A], tenDoubleThrows2 finit par être tapé comme Id. J'utilise copoint à la remettre en List type.

En bref, il semble être la clé de l'utilisation de l'état est d'avoir des fonctions de retour d'une fonction de modification de l'état et le résultat réel de la valeur désirée... Avertissement: je n'ai jamais utilisé state de la production de code, juste essayer d'obtenir une sensation pour elle.

Plus d'infos sur @ziggystar commentaire

J'ai renoncé à essayer en utilisant stateT peut être quelqu'un d'autre peut montrer si StateFreq ou StateRandom peut être augmentée pour effectuer le combiné calcul. Ce que j'ai trouvé, au contraire, est que la composition des deux transformateurs peuvent être combinées:

def stateBicompose[S, T, A, B](
      f: State[S, A],
      g: (A) => State[T, B]) = State[(S,T), B]{ case (s, t) =>
  val (newS, a) = f(s)
  val (newT, b) = g(a) apply t
  (newS, newT) -> b
}

Elle est fondée sur l' g être un paramètre de fonction prenant la suite du premier état du transformateur et le retour à un état du transformateur. Puis la suivante devrait fonctionner:

def diceAndFreqSum = stateBicompose(TwoDice, freqSum)
type St2[x] = State[(Random, Map[Int,Int]), x]
List.fill(10)(diceAndFreqSum).sequence[St2, Int].exec((new Random(1L), Map[Int,Int]()))

15voto

huynhjl Points 26045

Je suis tombé sur un blog intéressant post Grok Haskell Monade Transformateurs de sigfp qui est un exemple de l'application de deux monades par une monade transformateur. Voici une scalaz traduction.

Le premier exemple montre un State[Int, _] monade:

val test1 = for {
  a <- init[Int] 
  _ <- modify[Int](_ + 1)
  b <- init[Int]
} yield (a, b)

val go1 = test1 ! 0
// (Int, Int) = (0,1)

Donc, j'ai ici un exemple d'utilisation de init et modify. Après avoir joué avec elle un peu, init[S] s'avère être très pratique pour générer un State[S,S] de la valeur, mais l'autre chose qu'il permet d'accéder à l'état à l'intérieur de la pour la compréhension. modify[S] est un moyen pratique pour transformer l'état à l'intérieur de la pour la compréhension. Ainsi l'exemple ci-dessus peut être lu comme suit:

  • a <- init[Int]: démarrer avec un Int de l'état, définie comme la valeur enveloppé par l' State[Int, _] monade et de la lier à l' a
  • _ <- modify[Int](_ + 1): augmentation de l' Int état
  • b <- init[Int]: prendre l' Int de l'état et de la lier à l' b ( a mais aujourd'hui, l'état est incrémenté)
  • le rendement d'un State[Int, (Int, Int)] de la valeur à l'aide de a et b.

La compréhension de la syntaxe déjà fait trivial de travail sur l' A côté State[S, A]. init, modify, put et gets fournir quelques outils pour travailler sur l' S côté State[S, A].

Le deuxième exemple dans le billet de blog se traduit par:

val test2 = for {
  a <- init[String]
  _ <- modify[String](_ + "1")
  b <- init[String]
} yield (a, b)

val go2 = test2 ! "0"
// (String, String) = ("0","01")

La même explication que test1.

Le troisième exemple est plus difficile et j'espère qu'il y est quelque chose de plus simple que j'ai encore à découvrir.

type StateString[x] = State[String, x]

val test3 = {
  val stTrans = stateT[StateString, Int, String]{ i => 
    for {
      _ <- init[String]
      _ <- modify[String](_ + "1")
      s <- init[String]
    } yield (i+1, s)
  }
  val initT = stateT[StateString, Int, Int]{ s => (s,s).pure[StateString] }
  for {
    b <- stTrans
    a <- initT
  } yield (a, b)
}

val go3 = test3 ! 0 ! "0"
// (Int, String) = (1,"01")

Dans ce code, stTrans prend soin de la transformation de deux états (incrément et le suffixe avec "1") ainsi qu'en tirant l' String de l'état. stateT nous permet d'ajouter de la transformation d'état sur l'arbitraire d'un monade M. Dans ce cas, l'état est un Int qui est incrémenté. Si on appelait stTrans ! 0 on allait se retrouver avec M[String]. Dans notre exemple, M est StateString, donc nous allons nous retrouver avec StateString[String] qui State[String, String].

La partie la plus délicate est ici que nous voulons sortir de l' Int valeur de l'état de stTrans. C'est ce qu' initT . Il crée simplement un objet qui donne accès à l'état d'une manière que nous pouvons flatMap avec stTrans.

Edit: Transforme tous que la maladresse peut être évité si nous avons vraiment réutilisé test1 et test2 qui permet de stocker facilement le voulait états dans l' _2 élément de leur retournés tuples:

// same as test3:
val test31 = stateT[StateString, Int, (Int, String)]{ i => 
  val (_, a) = test1 ! i
  for (t <- test2) yield (a, (a, t._2))
}

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