olix / Mycel / Learn Mycel
in development

Learn Mycel: a short tour

Nine small lessons, each a real program with its real output. Open any of them in the playground and change it. No install needed.

Try Mycel in your browser →

1. Values

Values, types and f-strings

The first assignment creates a variable (x = 1); var makes one that changes. Types are inferred, so you rarely write them. f-strings format values into text, and {ratio:.2f} formats decimals. Division 10 / 4 produces 2.5; whole-number division is 10.div(4).

values.mycvariables, f-strings, arithmetic · open in playground
name = "Ada"
var visits = 1
visits += 1
ratio = 2.0 / 3.0
print(f"{name} has visited {visits} times")
print(f"ratio is {ratio:.2f}")
print(10 / 4, 10.div(4), 10 % 4, 2 ** 5)
output
Ada has visited 2 times
ratio is 0.67
2.5 2 2 32
2. Lists and loops

Lists, loops and comprehensions

A list is written [a, b, c]. for i, x in list gives the position and item. Negative indexes count from the end (fruits[-1]), and in tests membership. List comprehensions build new lists: [f.length for f in fruits]. 0..3 is a range, write prints without a newline, and sum and max are built in.

lists.myclists, comprehensions, built-ins · open in playground
fruits = ["apple", "pear", "fig"]
for i, fruit in fruits {
  print(f"{i + 1}. {fruit}")
}
print(f"last: {fruits[-1]}, pear in list: {\"pear\" in fruits}")
for n in 0..3 {
  write(f"{n} ")
}
print("")
lengths = [f.length for f in fruits]
print(lengths)
print(sum(lengths), max(lengths))
output
1. apple
2. pear
3. fig
last: fig, pear in list: true
0 1 2 
[5, 4, 3]
12 5
3. Functions

Functions and expressions

Functions start with fn. Parameters have types (inferred when omitted), defaults, and optional labels at call sites: greet("Bo", times: 2). if c { a } else { b } is an expression, returning a value directly without ternary syntax.

functions.mycfunctions, defaults, if-else values · open in playground
fn greet(name: String, times: Int = 1) -> String {
  var out = ""
  for _ in 0..times { out += f"hi {name}! " }
  return out
}

fn level(score: Int) -> String {
  return if score >= 90 { "expert" } else if score >= 70 { "pro" } else { "novice" }
}

print(greet("Ada"))
print(greet("Bo", times: 2))
print(f"score 85 is {level(85)}")
output
hi Ada! 
hi Bo! hi Bo! 
score 85 is pro
4. Missing values

Values that may be missing

A type ending in ?, such as Int?, may be nil, and the compiler makes you deal with that before you use it. if let unwraps it and ?? supplies a fallback.

optional.mycInt? and ?? · open in playground
fn find(names: [String], wanted: String) -> Int? {
  for i, n in names {
    if n == wanted { return i }
  }
  return nil
}

names = ["ana", "bo", "chen"]
if let i = find(names, "bo") {
  print(f"found at {i}")
}
print(find(names, "zed") ?? -1)
output
found at 1
-1
5. Enums and match

Enums and match

An enum lists the shapes a value can take, each with its own data. match picks the matching case and binds the data. It must cover every case. Leave one out and the program does not run (second example).

shapes.mycenum with data, exhaustive match · open in playground
enum Shape { Circle(r: Float), Rect(w: Float, h: Float) }

fn area(s: Shape) -> Float {
  return match s {
    .Circle(r) => 3.14159 * r * r
    .Rect(w, h) => w * h
  }
}

print(f"{area(Shape.Rect(w: 2.0, h: 3.0)):.2f}")
output
6.00
missing_case.myca forgotten case is a compile error · open in playground
enum Shape { Circle(r: Float), Rect(w: Float, h: Float) }

fn area(s: Shape) -> Float {
  return match s {
    .Circle(r) => 3.14159 * r * r
  }
}
compiler says
error [E0460]: this match does not cover every case
  --> missing_case.myc:4:10
  |
4 |   return match s {
  |          ^^^^^^^^^
  why: not handled: Shape.Rect(_, _)
  fix: add arms for Shape.Rect(_, _), or a catch-all `_ => ...`

1 error, 0 warnings
6. Classes

Classes

A class holds fields and methods. Create one by calling its name, with no new, and reach its own fields through self.

account.mycfields, methods, self · open in playground
class Account {
  var balance = 0
  fn deposit(amount: Int) {
    self.balance += amount
  }
}

acct = Account()
acct.deposit(50)
acct.deposit(25)
print(f"balance: {acct.balance}")
output
balance: 75
7. Errors

Errors you can see in the type

A function that can fail returns Result<Ok type, Error type>. Return Ok(value) or Err(reason), and put ? after a call to pass an error straight up to the caller.

errors.mycResult and ? · open in playground
fn parseAge(text: String) -> Result<Int, String> {
  n = Int.parse(text)
  if n == nil { return Err(f"'{text}' is not a number") }
  return Ok(n!)
}

fn nextYear(text: String) -> Result<Int, String> {
  age = parseAge(text)?
  return Ok(age + 1)
}

print(nextYear("41"))
print(nextYear("forty"))
output
Ok(42)
Err("'forty' is not a number")
8. Web apps

Web apps with std.web

Mycel includes std.web for web applications: routes with :params, HTML rendering, JSON APIs, and form handling. In production, app.run(8000) serves until stopped (needs --allow-listen).

web.mycroutes, HTML, JSON, form · open in playground
import std.web
import std.http

var messages = ["Hello from Mycel!"]
app = web.App()

app.get("/", req => {
  items = [f"<li>{web.escape(m)}</li>" for m in messages].join("")
  return web.html(f"<h1>Guestbook</h1><ul>{items}</ul>")
})

app.post("/sign", req => {
  text = req.form.get("message", "").trim()
  if text.isEmpty { return web.text("write something first", status: 422) }
  messages.push(text)
  return web.redirect("/")
})

app.get("/messages.json", req => web.json({"count": len(messages), "items": messages}))

// In production: app.run(8000) serves on port 8000
// Test live over HTTP:
server = (await app.start(0)).unwrap()
base = f"http://127.0.0.1:{server.port}"
print((await http.get(base + "/")).unwrap().body)
print((await http.get(base + "/messages.json")).unwrap().body)
server.stop()
output
<h1>Guestbook</h1><ul><li>Hello from Mycel!</li></ul>
{"count":1,"items":["Hello from Mycel!"]}
9. Mistakes

Mistakes are caught before the program runs

Types are checked up front. The compiler tells you what went wrong, where, and why, and usually how to fix it. Habits from other languages get the same treatment: see the guides for Python, JavaScript and TypeScript, Java, Go, Rust and Swift.

mistake.myca type mismatch is reported, nothing runs · open in playground
let age: Int = "fifteen"
print(age)
compiler says
error [E0300]: type mismatch: expected Int, found String
  --> mistake.myc:1:16
  |
1 | let age: Int = "fifteen"
  |                ^^^^^^^^^
  why: the initial value has type String, but Int is required here

1 error, 0 warnings
Safety

Programs start with no permissions

A Mycel program with no permissions is pure: it can compute and print, nothing else. Files, network, processes and environment variables are capabilities the host grants for each run (for example mycel run app.myc --allow-read=data), never assumed. That is why the playground can run anyone's code safely.

Status

What works, and what doesn't yet

Mycel is in development and not released: there is nothing to download or install today, so the playground is the way to try it. Every example on this page was run with the current compiler and the output shown is exactly what it printed. Details will change. A compatibility engine for Python, JavaScript/TypeScript, Java, Rust, Go and Swift packages is planned; installing foreign packages is not available yet. Files use .myc (.olx also works).

Next

Where to go from here

Read the guide for the language you already know, go back to the Mycel overview, or explore our fine-tuning datasets and materials for AI models.