# AP1 · 6 — Préparer la prochaine lecture (facultatif)

## Notre question

Que manque-t-il à notre calculatrice pour lire `"12.0+3.0"` ? Deux
expériences de lecture : des décimaux avec la bibliothèque, puis une
écriture plus courte de notre boucle avec `with`.

> **Repères.** Poly, chap. 4, section 4.5.2 « Enchaîner des résultats sans cacher les erreurs ». Le cours se fait ici, dans le carnet.

> 💡 Ce carnet est facultatif : il prolonge la séance pour ceux qui ont
> terminé la calculatrice. Durée indicative : 15 min. Il est autonome,
> et il n'est pas un devoir à faire entre AP1 et AP2.

Les cellules `assert` sont des vérifications fournies : si une condition
est fausse, Livebook montre le résultat obtenu et celui attendu.
Évaluez cet import une fois, puis réévaluez votre code avant ses tests
après chaque modification.

```elixir
import ExUnit.Assertions
```

## Les briques des carnets précédents

Ces deux modules sont ceux des carnets 4 et 5, dans leur version de
référence. Recopiez les vôtres si vous préférez : le contrat est le même.

```elixir
defmodule Token do
  def read("+"), do: {:ok, :plus}
  def read("-"), do: {:ok, :minus}
  def read("*"), do: {:ok, :multiply}

  def read(source) do
    case Integer.parse(source) do
      {number, ""} -> {:ok, {:number, number}}
      _other -> {:error, {:invalid_token, source}}
    end
  end
end
```

```elixir
defmodule Stack do
  def step({:number, number}, stack), do: {:ok, [number | stack]}
  def step(:plus, [right, left | rest]), do: {:ok, [left + right | rest]}
  def step(:minus, [right, left | rest]), do: {:ok, [left - right | rest]}
  def step(:multiply, [right, left | rest]), do: {:ok, [left * right | rest]}
  def step(_operator, _stack), do: {:error, :not_enough_operands}
end
```

## Lire un décimal ne définit pas encore un langage

`Float.parse/1` suit le même principe que `Integer.parse/1`. Prévoyez,
puis observez les nombres et les restes.

```elixir
{Float.parse("12.5+3.0"), Float.parse("12"), Float.parse("1.0e2"),
 Float.parse("chat")}
```

La bibliothèque peut accepter davantage de formes que notre futur
langage. En AP2, nous imposerons des chiffres de chaque côté du point
et nous refuserons l'écriture exponentielle. Ce sera le rôle du contrat
de lecture, pas une propriété à supposer de `Float.parse/1`.

## Alléger deux `case` avec `with`

Les deux `case` de la boucle du carnet 5 répètent le même geste :
poursuivre en cas de succès, renvoyer l'erreur sinon. `with` essaie les
motifs de gauche à droite ; si un résultat ne correspond pas, il est
renvoyé et la suite s'arrête. Voici un exemple complet à lire. Remplacez
la chaîne `"3"` par `"3x"` et prédisez si `Stack.step/2` sera appelée.

```elixir
with {:ok, token} <- Token.read("3"),
     {:ok, next_stack} <- Stack.step(token, [8]) do
  {:ok, next_stack}
end
```

```elixir
assert (with {:ok, token} <- Token.read("3x"),
             {:ok, next_stack} <- Stack.step(token, [8]) do
          {:ok, next_stack}
        end) == {:error, {:invalid_token, "3x"}}
```

Réécrivez maintenant la boucle de la calculatrice avec un `with` à la
place des deux `case`, puis relancez exactement les mêmes vérifications
que dans le carnet 5. Le corps du `do` appellera `walk(rest, next_stack)`.
La version avec `case` reste tout aussi correcte ; choisissez celle dont
vous savez expliquer les erreurs.

```elixir
defmodule RPN do
  def run(source), do: walk(String.split(source), [])

  defp walk([], [value]), do: {:ok, value}
  defp walk([], []), do: {:error, :empty_program}
  defp walk([], _stack), do: {:error, :extra_operands}

  defp walk([source | rest], stack) do
    # Remplacez ce commentaire par un `with` qui lit, applique, puis rappelle walk/2.
    {:error, {:todo, source, stack}}
  end
end
```

```elixir
assert RPN.run("1 2 3 * +") == {:ok, 7}
assert RPN.run("1 +") == {:error, :not_enough_operands}
assert RPN.run("12x 3 +") == {:error, {:invalid_token, "12x"}}
```

## Garder une trace

Notez en une phrase ce que `with` fait d'une valeur qui ne correspond à
aucun motif, et pourquoi la version avec `case` reste correcte. Ce
carnet est facultatif : gardez-le dans `entrainement/` si vous préférez.

Enregistrez ce carnet sous `AP1/ap1-06-prolongements.livemd` dans votre dépôt privé
`ap-homework`.

Un bout de syntaxe oublié ? Les chapitres 1 à 4 du [poly](https://ap-2026.noce.fr/poly/poly-elixir.pdf)
reprennent les formes du langage, chacune avec un exemple et son résultat ;
le cours, lui, se fait dans les carnets.

Pour toute question, vous pouvez me contacter par email :
[aurelien.noce@minesparis.psl.eu](mailto:aurelien.noce@minesparis.psl.eu).
