This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-05-29
Channels
- # announcements (15)
- # babashka (17)
- # beginners (66)
- # calva (18)
- # clojure (20)
- # clojure-dev (2)
- # clojure-europe (27)
- # clojure-nl (1)
- # clojure-norway (24)
- # clojure-sweden (5)
- # clojure-uk (4)
- # clr (1)
- # cursive (3)
- # datomic (10)
- # dev-tooling (9)
- # gratitude (1)
- # honeysql (5)
- # hyperfiddle (13)
- # malli (4)
- # music (1)
- # nrepl (1)
- # off-topic (31)
- # polylith (11)
- # portal (6)
- # re-frame (3)
- # reitit (4)
- # releases (1)
- # shadow-cljs (8)
- # squint (4)
- # tools-build (26)
- # vim (1)
- # yamlscript (52)
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...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
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...
I'll skip past performance for now (which doesn't bug me because everythihg can be made faster). The first buggy runs took 8s 🙂
I will point out that the clojure version of this run in the repl was even slower than the ys. weird
OK this destructure assignment bugs me because of the escpaing .
Things that you want to be scalars in YS but YAML considers them strings, you escape with a .
like a =: ."xxx" + foo
that would be invalid yaml without the .
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
then we can say let [a b c] =: ...
Look ma, no .
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?
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:
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):
but I can let this one marinate...
The next 2 things are bigger and they both involve if
forms
first you see this pattern a lot:
if ...:
do:
a: a
b: b
=>: c
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)
I had an idea to make this look friendlier:
if ...:
then:
a: a
b: b
else: c
that would compile the same.
The normal YS for if is:
if (x > 10):
say: 'x is big'
say: 'x is small'
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'
I will keep the current form
The 7 stage YS compiler has a stage called "transform"
and we can analyze the if
forms here and rearrange them
we currently transform cond from
cond:
a: b
c: d
to
cond %:
a: b
c: d
for example
We probably want to have rules like not allowing else
without then
etc
people can always use do
and =>
for the else
as needed (when there's no then
)
the next thing is pretty huge.
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
this pattern shows up a lot (and a lot in the weird-numbers.ys)
x =:
if (a > b):
inc: x
=>: x
$ ys -ce '
if (a > b):
x =: inc(x)
'
(if (> a b) (let [x (inc x)]))
is obviously wrong...but can we make it right?
iow can we reliably compile to something like:
(let [x (if (> a b) (inc x) x)] (say x))
This one probably needs a lot of thought and may very well be a bad idea
but it would make using YS for beginners (like me 🙂 ) so much easier
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)?
I think I can move forward on the then
and else
idea. The others need thought.
Makes me think of Haskell guards https://stackoverflow.com/questions/9345589/guards-vs-if-then-else-vs-cases-in-haskell condition comes first ..?
hmmm ys has multi-arity fns:
defn add:
(): 0
(x): x
(x y & xs):
add: (x + y) xs*
not sure how to read that hs (even though I used to know hs a bit)
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))
(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 elseOK. 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 🙂
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.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)