YS Testing Part 2
Yesterday we talked about writing tests in YS.
We also introduced the ys::taptest
library, but we didn't really cover how it
works.
Let's take a closer look...
Yesterday we talked about writing tests in YS.
We also introduced the ys::taptest
library, but we didn't really cover how it
works.
Let's take a closer look...
YS is a great language for writing tests in.
Why?
Well tests are mostly data. Inputs and outputs for some code you want to test. Code is usually in functions, and functions usually have names and names are just strings and strings are just data.
YAML is a great format for data. YS is YAML with code (expressed as YAML data).
So it seems like YS should be a great language for writing tests in. And it is.
YS is a YAML loader module. On one hand it does have super powers, but on the other hand it's just a plain YAML loader.
A YAML loader is a utility that can turn YAML into a data structure. Every modern language has a YAML loader. Most have many.
YS is the first YAML loader that was made for every programming language.
Well that's the goal anyway.
For the past year it's only had loaders for 11 languages: Clojure, Crystal, Go, Java, Julia, NodeJS, Perl, Python, Raku, Ruby, and Rust.
Until this week, that is...
Try running these commands on Linux or Mac:
git clone https://github.com/yaml/yamlscript
cd yamlscript
make install
Chances are that everything will just work.
You'll have new builds of ~/.local/ys
and ~/.local/lib/libys.so
(.dylib
on
a Mac).
YS actually has a lot of heavy dependencies like Clojure, Java, Maven, GraalVM, etc.
So how does it just work?
Today I'll tell you!
We've seen different ways to call YS functions in YAML.
Say we have this file.yaml
:
people:
- Alice
- Bob
- Charlie
What if we wanted to reverse the list?
The current version of YS as of today is 0.2.1
.
I'm sure you've noticed the v0
part of the !YS-v0
tag by now.
YS intends to be a "versioned programming language", where v2
can behave
very differently from v1
while still being able to use v1
libraries and
such.
Today I'll tell you a bit more about the versioning and how things are expected to work.
Remember, we are still officially at v0
so even the rules of how versioning
works are not yet set in stone!
I'd like to start off by saying that v0
is essentially stable at this point.
This means that YAML files starting with !YS-v0
should continue to work
without any changes.
Put another way... "Don't worry, be YS!"
Work is currently under way to put out a stable v1
release in the next couple
of months and then start work on v2
.
v1
?It might seem strange to only spend a couple of months on v1
.
It won't be much different from v0
when it is released.
Actually that's the point.
Declaring the stable version to be v0
doesn't make much sense.
At least it doesn't seem like it would spark much confidence.
Having a stable v1
out there is what many people are waiting for.
In reality it shouldn't be much different than how things stand today.
But it will be a promise.
The promise is that, once released, all code written for v1
will continue to
work in the future.
While v1
is under development, it will be available to use as !YS-v1-a
.
In essence, the v1
(short) dev period will be useful for ironing out the
details of creating a new YS version.
The v0
, v1
, v2
etc indicates the major version number (in the semver
sense) of the YS language version.
The plan is to use semver for the versioning.
v1
shows up in several places:
!YS-v1
tag#!/usr/bin/env ys-0
shebang lineys-0
and ys-0.2.1
binary namesSomehow any piece of YS code needs to know what version of YS it was written for.
When v3
comes out, it should be able to use libraries written for v1
, v2
,
or v3
.
It's an ambitious goal, but it should allow for a lot of flexibility and an ever improving language.
To pull this off, it is very fortunate that Clojure is extremely stable and backwards compatible.
In my personal experience, I've never seen Clojure code break when upgrading to a new major version of the language and/or tooling.
I wrote up a couple of internal documents for the YS team about v1 and versioning.
I'll share them with you now that the team has had a chance to review them:
These documents are still subject to change, but they are what we are working with now.
If you have any questions, or suggestions, please let us know.
You can start in the comments below.
Reusing code is the cornerstone of good programming.
If you see duplicate logic, refactor it into a function.
If you see a function that's used in multiple places, move it to a module.
If you need the module in multiple repos, publish it online.
Today let's see how we'd go about refactoring a YS program's useful functions into an external module for reuse by anyone.
All modern programming languages have regular expressions.
YS has them too.
Perl was the first modern language to include them as a built-in feature.
YS regexes borrow quite a bit from Perl's, but also behave as functions that return matching results (unlike Perl, and more like all the others).
Let's take a look...
Yesterday I showed you a FizzBuzz solution in YS. Here it is again:
!YS-v0
defn main(n=100):
each x (1 .. n): !:say
or? _ x:
str ((x % 3).! &&& 'Fizz'):
((x % 5).! &&& 'Buzz')
See that _
sitting there in the middle all by itself?
What's that all about?
Well _
is a special symbol in YS, and it means different things in different
contexts.
Last week I told you about YS on Rosetta Code and yesterday I told you about YS on Exercism.
Today I'm going to show you how to compare solutions to the same Rosetta Code task using all the Exercism languages!
Ever heard of a YS Exorcism?!
Sounds pretty scary!
A YS Exercism on the other hand is one of the best ways to learn YS.
Exercism.org is a free programming learning site with tracks for over 75 languages!
We know that YS is made from Clojure and the Clojure is made from Java. Clojure code is interoperable with Java code.
So is YS interoperable with Java?
Yes, YS is can call Java methods on its objects.
Let's see how to do it.
Today I'd like to show you why I love how YS handles symbols.
Symbols are used to represent variables, functions, and other names in the language.
All the shorties I showed you yesterday were symbols. Symbols that resolved to functions.
From the start, YAML has always been about making data clean and easy to read.
This carries over to YS code as well.
Even though YS code compiles to Clojure, YS often has shorter alternatives for Clojure's commonly used and longer function names.
Or better yet...
If I were to tell you that YS has a system,
to determine if something is true...
I'd deserve hell and fire,
and be labeled a liar,
for in total truth, YS has 2!
Yesterday we wrote a YS program by porting a Clojure program from Rosetta Code.
We introduced a bunch of new things without really explaining them.
Today I'll explain a set of them.
It's Friday and I feel like having some fun. With YS, of course.
Rosetta Code is a super fun site that has over 1000 programming tasks that people solve in nearly 1000 programming languages (including YS). If you've never heard of it, you should check it out.
Let's solve a task in YS that hasn't been solved yet!
I've mentioned YS "modes" in passing several times in this series.
YS has 3 modes: data
, code
, and bare
.
Fully understanding modes is one of the most important things to understand about YS.
Today I want to go deeper on the details of modes. This will make everything else much easier to explain going forward.
Yesterday we learned that all YS YAML input compiles to Clojure (Lisp) before being evaluated by a native binary Clojure interpreter runtime.
Does this mean that you could write Lisp functions in your YAML data files? And then call them on your data?
Of course it does!
What if I told you that...
Make sense?
There's almost nothing I like more about programming than one liners.
A one liner is a single line of code that does something useful and doesn't require any extra steps to compile or run.
You type one line, press enter, and get your result.
I first learned about one liners in Perl.
If we have a file.txt
with the following content:
one
two
three
four
five
Here's a Perl one liner that counts the number of lines in a file:
$ perl -E '@l = <>; say scalar(@l)' < file.txt
5
Yesterday we started learning about the ys
CLI and there's a lot more cool
stuff to learn about it.
But today I want to switch it up and talk about one of my favorite programs that I use many times a day.
You probably know about GitHub gists. They are one of the best ways to share text files with others.
Let's take a look!
ys
CommandThere are different ways to use YS but the most common is to use it via the YS
command-line tool: ys
; a very versatile tool indeed.
There's a lot you can do with ys
including using it like you would use jq
or
yq
one-liners:
$ jq .bar < <(echo '{"foo": 123, "bar": 456, "baz": 789}')
456
$ yq .bar < <(echo -e 'foo: 123\nbar: 456\nbaz: 789')
456
$ ys .bar < <(echo -e 'foo: 123\nbar: 456\nbaz: 789')
456
Each of these tools have their own advantages and we'll be diving deep into those waters soon enough.
Today let's just start by exploring the basic things you can do with the ys
CLI.
Do you know how a YS assignment actually works?
It compiles into a Lisp let
form.
Let's take a look...
Yesterday we looked at the cond
and case
functions.
The cond
function has a couple cousins: condp
and condf
.
Let's take a look at them.