Skip to content

Myron 0.3.0

Latest

Choose a tag to compare

@ncke ncke released this 13 Oct 22:26
· 2 commits to main since this release

Myron grows a macro system, modules, records and error handling, and the executable becomes a script runner as well as a REPL.

Some of the public Swift interface has changed, so a host written against 0.2.0 may need a few updates. See For Swift hosts below.

The language

Macros and templates. defmacro defines a macro, which receives its arguments as code and gives back new code to evaluate in their place. Templates, written with a backtick, build that code: ,x puts a value in and ,@xs splices a list in.

(defmacro (unless test then else) `(if ,test ,else ,then))
(unless false "yes" "no")                 ; "yes"

make-symbol and symbol? let a macro build the names it defines. Macros are not hygienic yet.

Modules. module gathers definitions behind a list of exports. import brings the exports into scope, and a qualified name reaches one without importing anything.

(module geo (area)
  (define (area w h) (* w h)))

(geo.area 3 4)                            ; 12

Records. make-record-type and make-record give named, fixed-shape values. get and put work on them as they do on hashmaps. A record type is identified by its name and fields, so two sessions that define point the same way agree on it.

(define point (make-record-type 'point '(x y)))
(get 'x (make-record point 1 2))          ; 1

Errors. raise signals an error of your own, and try catches it and hands the message to a handler.

(try (lambda (message) ($ "caught: " message))
     ((raise "boom")))                    ; "caught: boom"

Error messages can now end with hints, such as which conversion would let two numbers be compared.

Variadic parameters. A parameter written xs... collects the arguments the others leave over into a list.

(define (sum xs...) (reduce + 0 xs))
(sum 1 2 3)                               ; 6

Strings. string now joins any number of values, and $ is a shorter name for it: ($ "x = " 42) is "x = 42". The release also adds split-all, split-once and find-first.

More of the standard library:

  • sort and sort-descending
  • apply and foldr
  • zip and zip-all
  • flatten
  • range and range-len
  • take-last and drop-last
  • integers and integers-between
  • kind, something?, sortable? and comparable?
  • the constants nan and infinity

The myron executable

myron-repl is now myron, and it runs programs as well as the REPL:

  • myron --repl starts the REPL.
  • myron --load one.my --load two.my evaluates files in one environment. A #!/usr/bin/env -S myron --load line makes a script executable.
  • A .md file is literate Myron: its fenced lisp and myron blocks are the program.
  • myron --show file evaluates a file a form at a time and prints each result.
  • Programs get print, write, read-line, read-character, read-all, exit, load and arguments.

For Swift hosts

These changes may need a host written against 0.2.0 to be updated:

  • MyronSession.set(_:to:) now throws. It rejects a name that Myron source couldn't bind, such as "if", "nothing" or "two words", with invalidName, as define already did. Calls need try.
  • New enum cases. MyronValue and MyronValue.Kind gain record, recordType, module and macro. MyronError.Reason gains twelve cases, and MyronHigherOrder gains foldr and apply. A switch that covers every case will need the new ones.
  • Name rules. A special form's name, such as if, can no longer be bound, and nor can nothing, which is now a literal. A bound name can't end in a dot or contain a ".
  • Error messages. unexpectedType messages now quote the kinds they name. A host that matches on message text will need updating.
  • nan ordering. nan now sorts after every other double, so comparisons, sort, min and max treat it consistently.

New in the Swift interface:

  • MyronRecord, MyronRecordType, MyronModule, MyronMacro and MyronSignature
  • define(_:arity:body:), for primitives that take their arguments as an array
  • optional signatures on every define, such as "({integer} {string}[...])"
  • evalEach, which evaluates the top-level forms one at a time

A host primitive can now call eval on its own session. How deeply those calls can nest is bounded by the new maximumEvalDepth.

Fixes

  • A host primitive that called eval used to end the outer program early with the inner result. It now carries on where it left off.
  • The executable builds for Wasm again. The REPL banner had asked the terminal for its width in a way WASI doesn't support.

Full details are in the CHANGELOG, and every change is in the comparison with 0.2.0.
A few notes:

It’s a summary. The “For Swift hosts” section covers every breaking change the CHANGELOG records, and the CHANGELOG link at the bottom has the full detail.
The two links at the bottom will 404 until the tag exists. They point at the 0.3.0 tag, so push the tag before you publish the release.
No title is included. Put the tag name, 0.3.0, in GitHub’s release title field.