EnglishFrançais

Cookbook

Recettes idiomatiques pour les patterns courants en Amalgame. Chaque recette est un snippet auto-suffisant qu'on peut copier dans un fichier .am, compiler et exécuter avec amc build.

Pour la référence des classes / méthodes stdlib, voir 04-stdlib.md. Pour le langage lui-même, voir 02-language-tour.md.


Strings

Découper une string sur un séparateur

let line: string = "alice,30,paris"
let parts: List<string> = String_Split(line, ",")
// parts.Get(0) == "alice"
// parts.Get(1) == "30"
// parts.Get(2) == "paris"

Tester un prefix/suffix

if (String_StartsWith(name, "amalgame-")) { /* ... */ }
if (String_EndsWith(path, ".am"))         { /* ... */ }

Interpolation

let user: string = "Alice"
let age:  int    = 30
Console.WriteLine("hello {user}, you are {age}")
// → hello Alice, you are 30

L'interpolation accepte les chaînes d'appels : "count: {users.Count()}". Les sucres ${...} du shell ne sont pas reconnus — utiliser $NAME en string littérale si le but est de produire du bash.

Trim + lower

let cleaned: string = String_Lower(String_Trim(raw))

Joindre une liste avec un séparateur

let xs: List<string> = new List<string>()
xs.Add("a"); xs.Add("b"); xs.Add("c")
let joined: string = String_Join(xs, ", ")
// joined == "a, b, c"

Collections

List : itérer avec index

let users: List<User> = LoadUsers()
for i in 0..users.Count() {
    let u: User = users.Get(i)
    Console.WriteLine(String_FromInt(i) + ": " + u.Name)
}

List : itération directe

for u in users {
    Console.WriteLine(u.Name)
}

List : compréhensions

let adults: List<User>   = [u for u in users if u.Age >= 18]
let names:  List<string> = [u.Name for u in users]

Map<K, V> : compter les occurrences

let counts: Map<string, int> = new Map<string, int>()
for w in words {
    let prev: int = 0
    if (counts.Has(w)) { prev = counts.Get(w) }
    counts.Set(w, prev + 1)
}

Filter / Map fluent (closures)

let big: List<User> = users.Filter(u => u.Age >= 18)
let names: List<string> = users.Map(u => u.Name)
let total: int = numbers.Reduce(0, (acc, n) => acc + n)

Fichiers + JSON

Lire / écrire un fichier texte

let content: string = File.ReadAll("/path/to/input.txt")
File.WriteAll("/path/to/output.txt", content + "\nappendu\n")

Charger un JSON, lire un champ

import Amalgame.Json

let raw: string    = File.ReadAll("config.json")
let jv:  JsonValue = Json.Parse(raw)
if (jv.IsObject()) {
    let host: string = jv.Get("host").AsString()
    let port: int    = jv.Get("port").AsInt()
}

Sérialiser un map en JSON

let obj: JsonValue = JsonValue.NewObject()
obj.Set("name", JsonValue.NewString(user.Name))
obj.Set("age",  JsonValue.NewInt(user.Age))
let serialized: string = Json.Stringify(obj)
File.WriteAll("user.json", serialized)

CLI tools

Parser argv avec ArgParser

let ap = new ArgParser()
ap.Flag("-v")
ap.Flag("--verbose")
ap.Option("--output")
ap.Parse(Args_Count(), 1)

let verbose: bool = ap.HasFlag("-v") || ap.HasFlag("--verbose")
let out:     string = ap.GetOption("--output")
let files:   List<string> = ap.GetPositionals()

Lire une variable d'environnement avec default

var host: string = Env_Get("MY_APP_HOST")
if (String_Length(host) == 0) { host = "localhost" }

Exit code custom

public static void Main(string[] args) {
    if (problem) {
        Console.WriteError("oops")
        Exit_Set(2)
        return
    }
    // exit 0 par défaut
}

Processes

Capturer stdout + stderr d'une commande

let r = Process.RunCapture("git log --oneline -5")
if (r.Exit == 0) {
    Console.WriteLine(r.Stdout)
} else {
    Console.WriteError("git failed: " + r.Stderr)
}

Spawn streamable (v0.8.22+)

let p = Process.Spawn("tail -F /var/log/syslog", true)
while (p.IsAlive()) {
    let line: string = p.ReadLine()
    Console.WriteLine("> " + line)
}
let code: int = p.Wait()

Networking

TCP client : connect + send + receive

let cli = new TcpClient()
if (cli.Connect("example.com", 80)) {
    cli.SendString("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
    let resp: string = cli.ReceiveAll()
    cli.Close()
}

UDP socket : send + receive (avec from-address, v0.8.52+)

let sock = new UdpSocket()
sock.Bind(0)   // ephemeral port
sock.SetRecvBuf(8 * 1024 * 1024)   // 8 MiB

sock.SendTo("Hello", "127.0.0.1", 9000)

let pkt = sock.ReceiveFrom(1500)
if (pkt != null) {
    Console.WriteLine("from {pkt.FromIp}:{pkt.FromPort} → {pkt.Data}")
}

Patterns

Match avec arm guards

let classify: string = match n {
    0           => "zero",
    n if n > 0  => "positive",
    _           => "negative"
}

Algebraic enum + destructuring

public enum Shape {
    Circle(radius: float)
    Square(side: float)
    Rectangle(w: float, h: float)
}

let area: float = match shape {
    Shape.Circle(r)      => 3.14 * r * r,
    Shape.Square(s)      => s * s,
    Shape.Rectangle(w, h) => w * h
}

Try / catch / finally

try {
    DangerousOp()
} catch e {
    Console.WriteError("oops: " + e.Message)
} finally {
    Cleanup()
}

Null-safe access + coalescing

let name: string = user?.Name ?? "Anonymous"
let age:  int    = user?.Age  ?? 0

Guard clause

public void Process(input: string?) {
    guard input != null else { return }
    // input non-null à partir d'ici
}

Threading + async

Lancer un thread (amalgame-threading)

import Amalgame.Threading

Threading.ThreadSpawn(_ => {
    Console.WriteLine("hello from another thread")
    return 0
}, null)

Channel producteur/consommateur

let ch = Threading.ChannelNew(16)
Threading.ThreadSpawn(_ => {
    for i in 0..100 { Threading.ChannelSend(ch, i) }
    Threading.ChannelClose(ch)
    return 0
}, null)
while (!Threading.ChannelIsClosed(ch)) {
    let v: int = Threading.ChannelReceive(ch)
    Console.WriteLine("got: " + String_FromInt(v))
}

HTTP server fiber-driven (amalgame-net-http + amalgame-async)

import Amalgame.Net.Http
import Amalgame.Async

Http1.ServeAsync(8080, (req, res) => {
    res.SetStatus(200)
    res.WriteText("hello from fiber!")
})

Inline C

Petite primitive bas niveau

public class Hash {
    public static int FastHash(string s) {
        @c {
            const char* p = s ? s : "";
            unsigned int h = 2166136261u;
            while (*p) h = (h ^ (unsigned char)*p++) * 16777619u;
            return (i64) h;
        }
    }
}

Inclure un header système

@c { #include <sys/utsname.h> }

public class Sys {
    public static string Hostname() {
        @c {
            struct utsname u;
            uname(&u);
            return code_string_dup(u.nodename);
        }
    }
}

Templates / scaffolders

amc new myapp                              # exe (default)
amc new myapp --template service           # daemon (systemd/launchd/sc.exe)
amc new myapp --template ui-web-form       # webview GUI (amalgame-ui-web)
amc new myapp --template lib               # library
amc new myapp --template test              # standalone test bundle

Voir aussi