@markus.agwin I finally got the PR done: https://github.com/kloimhardt/LisRoot/pull/1
Adds simple make commands:
make docker-generate
make docker-runall
make open OPENER=...
make clean
etcYour notation clarifies a lot for me. I understand now that higher order functions of the Lisp form ((f 1) "a" 3) translate best to f(1) 'a': 3 (= syntaxA). Previously I considered two different notations: syntaxB) avoid parentheses at all using apply and syntaxC) use S-expression to denote higher order functions. I learned that one should not be afraid of parentheses in YS as long as they are used within YES-expressions. But I ask myself how the higher-higher Lisp form (((f 1) 2) "a" 3) translates best to YS. This higher-higher form occurs in @sritchie09’s Emmy algebra system which will be a cornerstone in replacing Python by YS in science :-). This is why I considered S-exprs (respectively apply) in the first place for higher order fns. So the questing remains: how to best translate this higher-higher form to YS?
Let's work through some real examples. Are you at keyboard?
yes
Cool. I made this for a starter:
!yamlscript/v0
defn f:
(x): \(x * %1)
(x y):
f: x * y
say:
f(6): 7
say:
f(6 2): 7
compiles to:
$ ys x.ys -c
(defn f ([x] (fn [& [_1]] (*_ x _1))) ([x y] (f (*_ x y))))
(say ((f 6) 7))
(say ((f 6 2) 7))
evals to:
$ ys x.ys
42
84I don't think we've discussed multi-arity functions yet. I think the syntax is pretty awesome.
Perfect higher order example functions don't jump to mind for me so maybe you can help
that one is HO at arity-1
but not sure how to make a chainable real example like your:
(((f 1) 2) "a" 3)iow, define your f please 🙂
an existing example from Emmy (i.e. the SICM book): if you complete the puzzle https://kloimhardt.github.io/cljtiles.html?page=97 you get the following code
(((Lagrange-equations (L-free-particle
'm))
(literal-function 'q))
't)Was looking for something simple that we could show all the code for here, then discuss various ways to call it.
But I can just riff on (((f 1) 2) "a" 3) without defining f I suppose...
The most usual case in Emmy is (((f 1 2) 3) 4) let's assume (defn f [a b] (fn [c] (fn [d] (+ a b c d)))
ok, thx. sec
I have:
#!/usr/bin/env ys-0
defn f(a b):
fn(c):
fn(d): (a + b + c + d)
say: (((f 1 2) 3) 4)
say:
apply:
apply:
f: 1 2
list: 3
list: 4I have this so far:
!yamlscript/v0
defn f(a b):
fn(c): \(+ a b c %1)
say: (((f 1 2) 3) 4)
say: ((f(1 2) 3) 4)
say: f(1 2).apply([3]).apply([4])
# say: f(1 2).eval(3).eval(4)I think I can make this work pretty easily:
say: f(1 2).(3).(4)
Which I like because the . operator inserts the LHS as the first arg for the RHS expression
like in
$ ys -e 'say: (5 .. 15).nth(5)'
10
(but normally in that specific case you would:
$ ys -e 'say: (5 .. 15).5'
10I think the f(1 2).(3).(4) is confusing. Maybe add a call function to ys which works like in JS?
In Clojure if you have (def x '(+ 1 2)) how do you evaluate x to get 3?
(eval x)
!yamlscript/v0
defn f(a b):
fn(c): \(+ a b c %1)
say: (((f 1 2) 3) 4)
say: ((f(1 2) 3) 4)
say: f(1 2).apply([3]).apply([4])
defn call [f & xs]: apply(f xs)
say: f(1 2).call(3).call(4)$ ys x.ys
10
10
10
10defn call(f & xs): apply(f xs)
currently mis-compiles 😞
will fix
but yeah I can add to ys::std
for completeness:
!yamlscript/v0
defn f(a b):
fn(c): \(+ a b c %1)
say: (((f 1 2) 3) 4)
say: ((f(1 2) 3) 4)
say: f(1 2).apply([3]).apply([4])
defn call [f & xs]: apply(f xs)
say: f(1 2).call(3).call(4)
say:
call:
call:
f: 1 2
=>: 3
=>: 4To be fair: I thought for some time how to express (((f 1 2) 3) 4) and that case is, I think, the real reason why S-exprs stuck around for such a long time. Can't be beaten. My best proxy ist the JS call. Or, and this is the outstanding speciality of YS: recommend to use S-exprs when it comes to higher order functions.
I find this last example without any parentheses very nice.
Well my first thought was:
f(1 2)(3)(4)which you could make a good YeS argument for, and is nicer than S to my eyes
But yeah, higher order S is fine 🙂
I can see the benefits of Lisp only having one way to do it, from both the learners side and the language side. But I'm a TMTOWTDI kind of person, and thus YS is as well...
I repeat that I think this
say:
call:
call:
f: 1 2
=>: 3
=>: 4
I the very best solution I ever saw for this problem.Cool. That's what I like to hear. Because for me I would never do that one. But YS is flexible enough for both of us 🙂
because the two call: at the beginning clearly indicates that a twice nested higher order function will follow. And then the actual call is very compact and readable.
BTW, I appreciate your bring this up because while refactoring the ytranslation.yaml file I realized that some things I wanted to do didn't work (yet).
Certainly the call is most readable form...
I guess depends who your audience is.
I'm used to writing libraries where I don't expect many end users to look. People who do look can grok tighter code more easily.
I'll add call now before I forget 😄
The f().call().call() is for OO programmers. Your staggered call syntax can be intriguing for Python mathematicians because the call does not disturb the calculation-notation.
One thing that bothers me about the . operator is that inserting into the 1st position is the wrong place for so many functions.
a-list.take(5) # wrong
a-list.take(5 _) # _ specifies where to put the arg, so worksbut I hate:
• needing the _ at all
• remembering arg order for multi arg core functions
that's why:
$ ys -e 'a-list.take(5)' -c
(__ a-list (list +take 5))
Certain functions like take compile to +take which runtime checks arg type and DTRT
Slower but painless.
If speed ever matters, you can be explicit:
$ ys -e 'a-list.take(5 _)' -c
(__ a-list (list take 5 '_))I think of Clojure as pretty low-level and performant and purposefully close to Java and I do love that about it. But I don't mind making speed tradeoffs in YS when it makes programming easier.
Also speaking of OO, there are a couple dozen big parts of YS that haven't even been broached yet. One is good OO.
Happy to hear suggestions if anyone has them...
Looked briefly at CLOS. Not sure how well it fits into Clojure
• I can only add my two cents here: there is a precise reasoning in the Clojure design concerning the issue whether a core function expects important data in the first or last arg position. I only forgot how the reasoning is exactly. But there is a blog post somewhere and the specialists certainly know. I only remember that there is a reason why one writes (conj coll x) but (cons x seq) . Has to do with sequences vs collections.
Yeah, I was watching a super old Rich interview this morning about various collections, their performance and how conj applies well to all.
But it's a constant struggle for me to remember arg order and types. And while I love Clojure (and its ecosystem) for what it offers me to accomplish things I've been marinating on for 20 years, I'm on the opposite side of those who want to use it for everything 🙂 iow, I don't study Clojure to have it be ingrained for a long time...
At some point I'm going to start writing parts of YS in YS. Maybe the ys::std lib first. But I also realize at this point it doesn't do much for me or ys to do so...
I have already started writing the repo tooling scripts in YS though.
Anyway back to your blog post idea....
I love the idea. Would you be interesting in collaborating on the post?
Sure I'd like to contribute. Question is: what? I already put everything I have into the ArXiv paper. I just wanted a post where Docker and the stuff I (and most scientists) do not understand can be learned about.
> A first step towards this would be: a small (few lines) blog post about 1) this --cpp idea and 2) your already existing Docker container for CERN's ROOT (did you use their container for this?). It could be one entry point to gauge a possible interest-level also in communities not used to Slack-channels. Such a blog post is in any case much less effort than immediately including a YS->C compiler.
Things I'd like to cover:
• Your LisRoot demo repo
• How it uses Docker so everyone can enjoy right away (compiling root crashed my (very new and capable) laptop twice!)
• How YAMLScript might be a better language than Python for this root stuff.
Things I'd like to do before posting:
• Finish the basic user docs (so interested people can play quickly)
• Add the trivial --cpp option
• Make a few improvements to the docker thing (I know how to make this awesome)
• Probably need to tweak the blog setup to monitor traction and also to point people to the best forums for further exploration
YS needs to find some early super-fans. This might help.
I really could use your help explaining why Clojure (with YS lipstick) is foundationally better for this kind of stuff than Python.
I strongly feel that YS/Clojure is (potentially) better than Python for most things, but I don't have the words for it yet...
I like what you wrote here: https://github.com/kloimhardt/LisRoot?tab=readme-ov-file#why-yamlscript
Makes me feel like your help would give the post a lot higher chance of connecting to the intended audience.
I am myself desperately searching for words to explain to my formerly-fellow scientists that notation also matters in coding and not only in math. And I think YAMLScript itself, being a new notation, enhances the available syntax considerably to this end. So: I will add an extended README.md to my LisRoot repository, it will basically be taken from the first part of the old ArXiv paper, but the code not in Clojure but in YAMLScript syntax. Then we'll see how it can be used in a YS blogpost.
Sounds good. I think between us we can work on the words, and also have a good feeling if it hits the mark. I do think that syntax/notation matters, but I also think that immutability and FP matters here, but I'm at a loss to explain exactly why...
Well, 1) calculation on paper and thus math is intrinsically a notation for immutable objects/entities/thingies. Putting this argument upside-down: if a programming-language (PL) is immutable, it suits best for math people. (BTW: Googles JAX makes Python immutable.) 2) Math formulas are expressions. If a PL is expression based (not statement based) -> good 3) Basic objects of math are functions (e.g. sin(x)) and operators (i.e. Integration operator). Now: operator is just another name for higher order function. If a PL is functional -> good
I wrote a draft. https://github.com/kloimhardt/LisRoot/blob/main/paper/why_ys.md It focuses on point (2) above (= expressions better than statements). Point (1) (= pure functions, immutability) cannot be shown with LisRoot as it obviously is meant for C++ object mutation. And point (3) (= higher order functions) is best demonstrated with YAMLScript+Emmy (in LisRoot higher order does not play a big role). Putting all 3 aspects into one post is too much anyway, they are completely distinct and independent.
Nice! Today is a travel day for me and I have a ton of menial tasks to do. I'll try to take a look at the airport or when I arrive to Madrid.
I also refactored https://github.com/ingydotnet/LisRoot/blob/main/ytranslation.yaml quite a bit. Interested to hear how you like it.
While working on @markus.agwin’s LisRoot with YS stuff, I've been thinking it should be easy to add a ys --cpp option to compile YS directly to C++.
A first step towards this would be: a small (few lines) blog post about 1) this --cpp idea and 2) your already existing Docker container for CERN's ROOT (did you use their container for this?). It could be one entry point to gauge a possible interest-level also in communities not used to Slack-channels. Such a blog post is in any case much less effort than immediately including a YS->C compiler.
> (did you use their container for this?) Yes. https://github.com/ingydotnet/LisRoot/blob/be8ec84a6dbccf61b0d83d746db2a315f9df40df/Dockerfile#L1
The c++ would just be a shell out to ferret. Simple.