Flux & généricité
Source: tutorial/02_flow_and_generics See in Playground
Génère les entiers 1..upper, les répartit de part et d’autre de threshold, décale chaque moitié d’un offset différent, puis fusionne les deux moitiés en un seul flux : le tout avec deux petits traitements génériques définis dans le même fichier et réutilisés deux fois chacun.
Exécution
cd tutorial/02_flow_and_generics
melodium run Compo.toml --upper 10 --threshold 5 --offset_above 100 --offset_below=-100Une valeur négative nécessite la forme --flag=valeur : --offset_below -100 échoue à l’analyse, car la ligne de commande interprète -100 comme un groupe d’options courtes plutôt que comme une valeur.
En exécutant avec --upper 10 --threshold 5, le flux at-or-below-threshold journalise 1, 2, 3, 4, 5 et above-threshold journalise 6, 7, 8, 9, 10 : le découpage symétrique qu’implique 1..10 autour de 5.
Optionnel : ajoutez --api-report et un jeton d’API (MELODIUM_API_TOKEN) pour voir la trace complète de cette exécution sur Cadence.CI .
Fonctionnement
Cet exemple non plus n’utilise aucun modèle : tout est traitement sans état, fonction, et deux traitements génériques maison.
Flux de données
Construction de la séquence
1..upper est construit avec generate, qui produit upper valeurs de remplissage, et count, qui les numérote à partir de 1 :
howMany: emit<u128>(value=upper)
seed: generate<u128>(data=0)
numbering: count<u128>()
asI64: saturatingToI64<u128>()
startup.trigger -> howMany.trigger,emit -> seed.length,stream -> numbering.stream
numbering.count -> asI64.value,into -> split.valueUn traitement générique pour la répartition : aboveThreshold<N: PartialOrder>
aboveThreshold n’est instancié qu’une seule fois (sous le nom split) mais il est écrit de façon générique : il ne mentionne jamais i64 dans son corps, et fonctionne pour tout type ordonnable (PartialOrder). À l’intérieur, il construit un flux de même longueur rempli de la valeur threshold grâce à toVoid et fill, afin de pouvoir comparer élément par élément avec greaterThan, puis utilise ce flux de booléens pour répartir l’entrée en above/below avec filter :
treatment aboveThreshold<N: PartialOrder>(const threshold: N)
input value: Stream<N>
output above: Stream<N>
output below: Stream<N>
{
asVoid: toVoid<N>()
thresholdSt: fill<N>(value=threshold)
isAbove: greaterThan<N>()
partition: filter<N>()
Self.value -> asVoid.value
asVoid.iter -> thresholdSt.pattern
Self.value -> isAbove.a
thresholdSt.filled -> isAbove.b
Self.value -> partition.value
isAbove.is -> partition.select
partition.accepted -> Self.above
partition.rejected -> Self.below
}Self.value est lu trois fois ici (vers asVoid, isAbove, et partition) : lire plusieurs fois la même entrée est autorisé, seule l’écriture répétée sur une même entrée ne l’est pas.
Un second traitement générique : shift<N: Add>
shift est instancié deux fois (bumpAbove, bumpBelow) avec deux valeurs offset différentes : même traitement, même astuce toVoid + fill, qui alimente cette fois add :
treatment shift<N: Add>(const offset: N)
input value: Stream<N>
output shifted: Stream<N>
{
asVoid: toVoid<N>()
offsetSt: fill<N>(value=offset)
adder: add<N>()
Self.value -> asVoid.value
asVoid.iter -> offsetSt.pattern
Self.value -> adder.a
offsetSt.filled -> adder.b
adder.sum -> Self.shifted
}bumpAbove: shift<i64>(offset=offset_above)
bumpBelow: shift<i64>(offset=offset_below)
split.above -> bumpAbove.value
split.below -> bumpBelow.valueFusion en un seul flux
merge entrelace les deux flux décalés sans ordre prévisible : en relançant le programme, l’ordre des journaux peut différer, ce qui est normal. Les flux Mélodium n’offrent aucune garantie d’ordre implicite entre branches :
combined: merge<i64>()
bumpAbove.shifted -> combined.a
bumpBelow.shifted -> combined.bUn dernier count numérote le flux fusionné, simplement pour montrer que c’est un flux comme un autre.
Dépendances
[dependencies]
std = "0.10.3" # flux de base, journalisation, structures de données