Your first program
This is the ten-minute path from an empty file to a typed, verified AI call. No API key is needed for any of it.
1. Hello
# hello.ecko
print("Hello, AI World!")
ecko hello.ecko
2. Values and functions
name = "Ada" # a binding
mut count = 0 # explicitly mutable
count = count + 1
fn greet(who) = "Hello, {who}!" # single-expression function
print(greet(name)) # Hello, Ada!
Strings interpolate with {expr} directly. Functions can be one expression with =, or a block with { }.
3. Collections and pipelines
scores = [3, 9, 4, 1]
top = scores
|> filter(fn(s) s > 2)
|> sort()
|> reverse()
print(top) # [9, 4, 3]
|> feeds the value on the left into the call on the right. It binds loosest of all operators, so a pipeline reads top to bottom.
4. Your first ai call
ai is a keyword. There is nothing to import and nothing to configure:
answer = ai "Name a colour"
print(answer)
Run it. With no API key you get [AI Mock] Name a colour - deterministic mock mode, which is what makes the rest of this page testable offline.
5. Typed output
An untyped call returns text. Ask for a type and you get that type:
count = ai[Int] "How many days in a week?"
print(count + 1) # arithmetic on the result, not on a string
The type is enforced by coercion through a schema. ai[Int] is an Int or the call fails - it is never the string "seven". Structs, enums and lists work the same way:
type Sentiment = Positive | Negative | Neutral
mood = ai[Sentiment] "The service was wonderful"
See ai and typed output for the full set.
6. A contract
Types catch type errors. Contracts catch the rest:
@ensures(len(result) <= 80)
fn headline(article) = ai "Headline for: {article}"
print(headline("A cat was rescued from a tree."))
If the model returns something too long, Ecko feeds the failure back and retries rather than handing you bad output. Contracts can also be written in natural language, checked by a model. See Contracts.
7. Run it as a test
Rename the checks into a test file and ecko test runs them in forced mock mode - deterministic, offline, no key:
# hello_test.ecko
import std.test
test.case("greeting", fn() {
test.eq(greet("Ada"), "Hello, Ada!")
})
ecko test
Where to go next
- Mock mode - how all of this ran without a key
- Providers & configuration - adding a real key
- The language - Ecko as a general-purpose language
- The REPL - trying things without a file