Meow Programming Language v0.20.0: A Value Read Out of Go Remembers Where It Came From
Released 2026-08-19. nab go lets a .nyan program reach a Go package with no
wrapper written for it. In v0.20.0 the value that comes back stops forgetting
what it is.
Reading a Go value was what made it unusable
The rule ran backwards: the more of a Go type Meow could read, the less of it
was left to use. A *url.URL was read into a basket, and once it was a basket
the methods of the URL were gone.
nab go "net/url" tag u
nyan p = u.parse("https://example.com/a/b?x=1")
nya(p["host"]) # example.com — readable
nya(p.string()) # Hiss! Cannot call string on a Map, nya~
Four shapes lost something this way:
| Read as | What was lost |
|---|---|
*url.URL → basket | .string(), .hostname(), .query() |
time.Time → its text | .year(), .format(), .before(), .add() |
time.Duration → an integer | .minutes(), .seconds() |
url.Values → basket | .get(), .encode() |
What is read now remembers what it was read out of
A member Meow has nothing for is asked of the Go value itself, so both questions have an answer:
nab go "net/url" tag u
nab go "time"
nyan p = u.parse("https://example.com/a/b?x=1")
nya(p["host"]) # example.com
nya(p.hostname()) # example.com
nyan d = time.ParseDuration("90m")
nya(d) # 5400000000000
nya(d.minutes()) # 90
Only what was read remembers. A basket a program wrote itself remembers nothing, and a plain string or a plain number keeps nothing either — there is no more of it to reach.
Handing a value on keeps what the reading did not say
A time.Time used to cross into the next call as RFC 3339 text and be parsed
back, arriving up to a second wrong. It now crosses as the time.Time it is:
nab go "time" tag t
nyan a = t.Date(2026, 1, 2, 3, 4, 5, 500000000, t.UTC)
nyan b = t.Date(2026, 1, 2, 3, 4, 5, 0, t.UTC)
nya(a) # 2026-01-02T03:04:05Z
nya(b) # 2026-01-02T03:04:05Z — the same text
nya(a.sub(b)) # 500000000 — not the same time
A method with nothing to say gives back what it was called on
Go has many methods called to do rather than to say — Set, Add, Reset,
Close. They all answered catnap, which left the doing with nowhere to show.
The change now arrives as a new value beside the old one, which is how a
language of unchanging values says that something happened:
nab go "net/url" tag u
nyan q = u.ParseQuery("x=1&y=2")
nya(q.set("z", "9")) # {x: [1], y: [2], z: [9]}
nya(q) # {x: [1], y: [2]}
Having no answer is still not the same as answering with nothing: a search that
found nothing answers catnap, and a method that failed answers with the
furball.
Two smaller changes ride along. A value read out of Go now satisfies a Go
interface it implements, so a named integer whose String() is callable is
accepted where a fmt.Stringer is asked for. And nya(basket) sorts its keys,
which it had not done — it iterated a Go map, so it printed differently between
runs.
Upgrading
Nothing to change. Every guard that refused before still refuses: a
program-written basket still gives Cannot call string on a Map, a plain
string still refuses, and d == 5400000000000 is still true. Code that worked
around the gap by pulling fields out of a basket keeps working; it can now be
written as the method call it wanted to be.
brew upgrade meow
# or
go install github.com/135yshr/meow/cmd/meow@v0.20.0
See Importing a Go package in the specification for what comes back and how it is read.