Skip to content

slate 0.0.26

Choose a tag to compare

@edadma edadma released this 04 Sep 22:56
· 105 commits to dev since this release

What changes behaviour

Read these first: each one refuses or reshapes something 0.0.25 accepted.

hash, hashStrong and check in slate:password answer promises. Hashing a password is
supposed to take a tenth of a second and a loop is one thread, so on the loop that was a tenth of a
second in which the server answered nobody — ten simultaneous logins were ten seconds of a dead
process. The derivation now runs on libuv's thread pool. await hash(p) and await check(r, p) is
what a login path writes.

  • The promises carry the value, not a result. await hash(p) is the record and await check(r, p)
    is a boolean, where a file read answers { ok, value }. A value the program built cannot fail to be
    read back, and the failures it does have are faults raised where the call is written.
  • needsRehash is unchanged and synchronous. It reads the parameters out of a record and compares
    them — microseconds, no derivation — so a promise there would be ceremony.
  • A malformed record is still a fault, raised before anything is queued. A corrupted row has not
    become a rejected promise.

Every argument must fit the type its type parameter was solved to. pair[T](a: T, b: T) given
pair(1, "x") is refused, naming the parameter, what it was taken to be, and the argument that
disagrees. This reverses 0.0.25's "a conflict widens into a union": widening made such a call mean
something the reader almost never wanted. A union is still an answer where the program declared one.

An object literal written where a shape is expected may carry only the fields that shape names.
use({ colour: "red" }) at a parameter wanting { color: string } is refused, and the message names
the near spelling. Only a literal at the spot — a value reaching the same parameter through a name is
structural as before, a literal built by a spread is a merge rather than a literal, and a literal
nested inside one is untouched. It is TypeScript's excess-property check, drawn in the same place: a
literal built for this call serves nobody else, so an extra field in it is intentional and wrong.

close(server) ends the connections the server accepted, not only the listening socket. A program
that had closed everything it owned could still not exit. close now stops the server accepting,
closes every idle connection at once, and lets one with a request in flight finish that response.
There is no keep-alive after close. The HTTP/2 speaker gained the idle clock the HTTP/1.1 speaker
has always had.

upper and lower are ASCII again. sysl 0.0.101 quietly made sysl.text.to_upper walk the
Unicode database, so upper("héllo") became HÉLLO under the interpreter while slate js still
answered HéLLO. The two back ends have to agree, and ASCII is what the JavaScript one has always
documented itself as doing.

What is faster

A local and a parameter are numbered slots on the operand stack, rather than names in a scope
object built per call. A chunk that needs no scope at all builds none; a chunk that needs one for a
single captured name still puts the rest of its locals in cells; and a block pushes a scope only
where it declares a name into one.

Measured against the globals.sl control, on alternating builds of the same programs: calls.sl
442 → 279 ms, closures.sl about 47% off, nested.sl about 10%. arith.sl does not move, its loop
having no parameters in it.

Also

  • A val shadowing a parameter is legal slate and was a JavaScript syntax error.
  • Four checker fixes found by writing real programs against it.

Installing

brew update
brew upgrade slate
which slate
brew test slate