Fork me on GitHub
#yamlscript
<
2024-05-29
>
Ingy döt Net12:05:26

I just finished writing a YAMLScript program for: https://rosettacode.org/wiki/Weird_numbers

$ time ys weird.ys
The first 25 weird numbers:
70 836 4030 5830 7192 7912 9272 10430 10570 10792 10990 11410 11690 12110 12530 12670 13370 13510 13790 13930 14770 15610 15890 16030 16310 

real    0m1.195s
The YS code is: https://gist.github.com/ingydotnet/5ea3299279aeff3abf875df5a605c1f5 There wasn't an existing Clojure entry so I ported my entry from the Ruby one: https://gist.github.com/ingydotnet/246142af10c2a70a421bd24bb220b9d9 This might seem like cheating but my goal was not to solve the problem but to see how well I could express it in YS. This turned out to be a very useful exercise because even though the result looks reasonable, there are several things about it that annoy my adhd brain. I'd like to talk about them in a thread...

🧵 2
Ingy döt Net12:05:29

The elephant in the room is the 2s runtime. Ruby is 10x faster, and ruby isn't fast. I suspect that most of the time is spent manipulating 17000 entry vector. I wanted to use a Java array but YS has a compile issue currently with the clojure . interop

Ingy döt Net12:05:36

https://gist.github.com/ingydotnet/5ea3299279aeff3abf875df5a605c1f5#file-weird-ys-L56 that function call is almost all the time, but also calls most of the code...

Ingy döt Net12:05:32

I'll skip past performance for now (which doesn't bug me because everythihg can be made faster). The first buggy runs took 8s 🙂

Ingy döt Net12:05:15

I will point out that the clojure version of this run in the repl was even slower than the ys. weird

Ingy döt Net12:05:24

OK this destructure assignment bugs me because of the escpaing .

Ingy döt Net12:05:44

Things that you want to be scalars in YS but YAML considers them strings, you escape with a . like a =: ."xxx" + foo

Ingy döt Net12:05:59

that would be invalid yaml without the .

Ingy döt Net12:05:11

but on a key it bugs me

👍 1
Ingy döt Net12:05:24

I had the idea for YS def/let syntax of a =: 1 you could add an optional let in front. so let a =: 1 would could the same

Ingy döt Net12:05:49

then we can say let [a b c] =: ...

Ingy döt Net12:05:19

The next thing is also minor but also probably easy to address: https://gist.github.com/ingydotnet/5ea3299279aeff3abf875df5a605c1f5#file-weird-ys-L25 Do we need parens around an if condition in the common case?

Ingy döt Net12:05:00

the general pattern for if is if cond-form: then-form else-form technically you could have: if a b: c or if a b c:

Ingy döt Net12:05:49

I think if we see a key of if balanced-paren-form: ... we leave alone if we see if some stuff: ... we change it to if (some stuff):

Ingy döt Net12:05:00

but I can let this one marinate...

Ingy döt Net12:05:09

The next 2 things are bigger and they both involve if forms

Ingy döt Net12:05:17

first you see this pattern a lot:

if ...:
  do:
    a: a
    b: b
  =>: c

Ingy döt Net12:05:52

the do is needed when there are more than one form and the =>: is needed when the form is a scalar (to make YAML happy) (the => evaporates during compilation)

bsb12:05:07

fwiw, I agree on the ."abc" ugliness.

👍 1
Ingy döt Net12:05:11

I had an idea to make this look friendlier:

if ...:
  then:
    a: a
    b: b
  else: c

Ingy döt Net12:05:27

that would compile the same.

bsb12:05:38

Naively I'd expect (ah... you just said)

if ...:
  then:
  else:

bsb12:05:51

good to converge

Ingy döt Net12:05:41

The normal YS for if is:

if (x > 10):
  say: 'x is big'
  say: 'x is small'

bsb12:05:20

with do to have a block of statements

Ingy döt Net12:05:21

but this could be done now:

if (x > 10):
  =>:
    say: 'x is big'
  =>:
    say: 'x is small'
or maybe later:
if (x > 10):
  then:
    say: 'x is big'
  else:
    say: 'x is small'

Ingy döt Net12:05:32

I will keep the current form

Ingy döt Net12:05:42

The 7 stage YS compiler has a stage called "transform" and we can analyze the if forms here and rearrange them

Ingy döt Net12:05:51

we currently transform cond from

cond:
  a: b
  c: d
to
cond %:
  a: b
  c: d

Ingy döt Net12:05:19

We probably want to have rules like not allowing else without then etc

Ingy döt Net12:05:22

people can always use do and => for the else as needed (when there's no then)

Ingy döt Net12:05:14

the next thing is pretty huge.

bsb12:05:17

new thread?

Ingy döt Net12:05:02

in clojure you need to say

(let [x y
      x (if (> a b)
          (inc x) 
          x)]
  (say x))
in python you would
x = y
if (a > b):
  x = x + 1
say(x)
in ys:
x =: y
x =:
  if (a > b):
    inc: x
    =>: x
say: x

Ingy döt Net12:05:04

this pattern shows up a lot (and a lot in the weird-numbers.ys)

x =:
  if (a > b):
    inc: x
    =>: x

Ingy döt Net12:05:59

$ ys -ce '
if (a > b):
  x =: inc(x)
'
(if (> a b) (let [x (inc x)]))
is obviously wrong...

Ingy döt Net12:05:06

but can we make it right?

Ingy döt Net12:05:17

iow can we reliably compile to something like:

(let [x (if (> a b) (inc x) x)] (say x))

Ingy döt Net12:05:28

This one probably needs a lot of thought and may very well be a bad idea

Ingy döt Net12:05:02

but it would make using YS for beginners (like me 🙂 ) so much easier

Ingy döt Net12:05:22

Is there a better way in clojure to conditionally change a binding without using (let [old (if cond new old)] ...) (needing to repeat old as the else form)?

Ingy döt Net13:05:09

I think I can move forward on the then and else idea. The others need thought.

Ingy döt Net13:05:03

hmmm ys has multi-arity fns:

defn add:
  (): 0
  (x): x
  (x y & xs):
    add: (x + y) xs*

Ingy döt Net13:05:47

not sure how to read that hs (even though I used to know hs a bit)

Markus Agwin14:05:22

One could use cond-> instead of if, although the code does not get shorter

(def y 5)
(let [x (if (> 1 0) (inc y) y)] (say x))
(let [x (cond-> y (> 1 0) inc)] (say x))

1
bsb14:05:57

(was away) The interesting haskell bit is the

x | cond1 = val1
  | cond2 = val2
where conditions are not arities but runtime checks (like if-then cascades or clojure cond) More for syntax inspiration than anything else

Ingy döt Net14:05:03

OK. Right. This could be a possibly exciting way to add runtime/type/condition polymorphism (with a sexy syntax) to YS. I'll definitely add that to my hammock list 🙂

Ingy döt Net14:05:09

Reminds me of:

$ ys -ce 'defn foo(x y=42): x + y'
(defn foo ([x y] (+_ x y)) ([x] (foo x 42)))
which provides clean arg defaults by generating a multi-arity result.

Ingy döt Net18:05:33

Some more things to improve from weird.ys... • need to support w.$n to replace w.nth(n) • need a != operator for a != b instead of not(a == b)