News for hackers. Stuff that matters.
Re:The Janet Language by crabbone on Saturday February 18, 2023 @06:43AM (#34846125)
One thing that bothers me about languages that compose functions like this f(g(x)) is that when programming interactively f is typically an afterthought. So, you start out with g(x) and then need to go all the way back and add f. Similarly, when x turns out like it needs to be elaborated, and you either have to go all the way back, or edit the expression in place making it longer and inevitably facing problems with terminal length and terminal input being determined by when you press "Enter".

I like Lisp languages, and would take Scheme over Python for my job in a heartbeat, if I was allowed to. But, I think, that if we want interactive Shell-like programming, we really need to address the issues above in the language. Shell pipes are a good start, but they are quite restricted in what they can do and require xargs "extension". Some languages also have the "where" form, where arguments to a function can be elaborated after the function call.

If I was ever to design a language for interactive programming, I'd certainly try to have these two features in it.

Re:The Janet Language by lispm on Saturday February 18, 2023 @07:10AM (#34846290)
The 'terminal' restriction is long gone. Most Lisp read-eval-print-loops run inside an editor or another specialized tool.

In Common Lisp:

  CL-USER 37 > (sin 3)
  0.14112

  CL-USER 38 > (cos *)
  0.9900591
Above is acually is (cos (sin 3)). The variable * is bound to the last result.
Re:The Janet Language by crabbone on Saturday February 18, 2023 @01:32PM (#34849591)
How'd you go about multiple-value-bind? Same problem with handler-case.

When used interactively, Python also has _ to store the previous value (but Python only ever really returns single value, which is sometimes a tuple or a list that can be deconstructed into variables, iirc in CL if you don't request other return values, they are gone.)

More generally, you'd want more of xargs-like functionality (eg. split result into chunks and feed them to the next function in chunks, perhaps in parallel). Or maybe you'd want something like a tee, to split the results of the previous function into multiple streams and processed by different functions? Java-like languages don't immediately support something like that, but Shell-like do with redirection syntax, tee, xargs.

Re:The Janet Language by lispm on Saturday February 18, 2023 @03:08PM (#34850537)
> Java-like languages don't immediately support something like that, but Shell-like do with redirection syntax, tee, xargs.

But in Lisp you are not bound to the language syntax of Java. You can inside the language write tools to process forms. That's one of the main differences between Java and Lisp. Lisp has reader macros (to change the surface syntax of s-expressions) and macros to change the expression syntax. That would allow you to write tools to process code and results in arbitrary ways.

Multiple-values results in a REPL are handled by the variable /. That one is the list of the last values.

  CL-USER 4 > (values 1 2 3)
  1
  2
  3

  CL-USER 5 > (multiple-value-bind (a b c) (values-list /) (list a b c))
  (1 2 3)
But anyway, I would not write Common Lisp in a pure terminal without editing support.

Something like GNU Emacs can use tools like SLIME or has a SHELL mode. That one works fine in a terminal.

SLIME is one of the Common Lisp IDE extensions for GNU Emacs and works fine in a terminal. Writing even the most complex code inside an GNU Emacs & SLIME terminal session is no problem at all. If I have a function call (foo a) and I want to wrap code to the front, I would just move the cursor one s-expression back and type. I'm sure that's easier with one of the other editor extensions which make editing s-expressions kind of structural.

EVEN then, many people write the actual Lisp code in an editor buffer (say GNU Emacs in a terminal connected to a Lisp process via SLIME) and send the expression for evaluation to a connected or underlying interactive Lisp sessions. The editing of s-expressions in a terminal to move forward, backward, upward, etc is really a non-issue.

Interactive read-eval-print-loops does not mean one is forced to use a terminal without editing support.

Re:The Janet Language by crabbone on Sunday February 19, 2023 @06:20AM (#34856600)
I've been using CL and SLIME for over 10 years and had no idea about special meaning of "/"... TIL.

Anyways, the problem with handler-case still stands, as well as the other aspects s.a. chunked output, tee and redirects. It's something that would have to be programmed on top of the existing stuff, which was my point originally.

Re:The Janet Language by adrianmsmith on Saturday February 18, 2023 @06:47AM (#34846153)
> So, you start out with g(x) and then need to go all the way back and add f.

That's one thing I will say after coming from Perl/PHP to Java, is that despite its verbosity and the uselessness of having to write .stream(), I much prefer Java's stream.map(...).filter(...) syntax over the more functional-style filter(map(list, ..), ..) syntax. The Java syntax reads left-to-right, which is the order you want when you're thinking about code, and also as you say writing it. I think if I were creating a programming language I too would try to make stuff read left-to-right as much as possible.

Re:The Janet Language by nordsieck on Saturday February 18, 2023 @07:40AM (#34846467)
In addition to the examples other people have given, I think Ruby does this very nicely by making map, filter, and reduce methods on collection data structures.

Of course, Ruby is a bit of a mixed bag, but for the applications where it fits, it can be very nice.

Re:The Janet Language by mst on Saturday February 18, 2023 @07:12AM (#34846306)
It's notable that raku (previously known as perl6) lets you write things in either direction, if I remember correctly something like

  @source >>> map { ... } >>> grep { ... } >>> my @sink;
though note I'm typing from memory on my second coffee so I may have got that slightly wrong.

Plus of course there's many languages with a |> operator so you can do

  g(x) |> f
I also (the example is specialised for I/O but the implementation technique could trivially be borrowed for something that wasn't) implemented something sort of in this direction for perl once: http://p3rl.org/IO::Pipeline

I'm not convinced that left-to-right is -always- the best option and prefer having the choice of both, but I wouldn't be at all surprised if a survey of developers found that if they could only pick one they'd pick left-to-right, and while I'd find it hard to choose for myself alone I'd probably pick left-to-right on the basis that it'd likely make it easier to onboard people to any given codebase.

Re:The Janet Language by tmtvl on Saturday February 18, 2023 @11:08AM (#34847927)
Raku also has map and grep methods for collections, so it could be written as...

  my @sink = @source.map({...}).grep({...}).sort;
Which also makes multithreading the operation easy:

  my @sink = @source.hyper.map({...}).grep({...}).sort.list;
That said, I think the operator you're looking for is the feed operator, ==>:

  my @sink;
  @source ==> map {...} ==> grep {...} ==> sort ==> @sink;
It also has the corresponding reverse, <==, because why not. The docs* mention that ==> may at some point do automatic parallelization, but I don't know what the status of that feature is.

* https://docs.raku.org/routine/==%3E

Re:The Janet Language by mst on Sunday February 19, 2023 @08:41AM (#34857472)
> That said, I think the operator you're looking for is the feed operator, ==>

Yes, yes it was.

Clarifications and elaborations much appreciated.

Re:The Janet Language by ocimbote on Saturday February 18, 2023 @07:10AM (#34846293)
Aligning reading order with flow of actions is so important!

That's why I use and msybe tend to abuse the `->` and `->>` macros in Clojure and the pipe operator `|>` in Elixir.

Hopefully soon in JS as well, if I've read correctly.

Re:The Janet Language by temporallobe on Saturday February 18, 2023 @10:22AM (#34847531)
Not sure about other Lisp languages, but Clojure has thread operators that allow you to compose function calls this way, allowing you to visualize code in that preferred left-to-right (or top-to-bottom) order.
Re:The Janet Language by twism on Saturday February 18, 2023 @07:02AM (#34846236)
Maybe what you are looking for is threading https://stuartsierra.com/2018/07/06/threading-with-style
Re:The Janet Language by tincholio on Saturday February 18, 2023 @08:30AM (#34846802)
I'm not familiar with Janet, but I know it's Clojure-inspired, so it probably has threading macros, which are like shell pipes on steroids. In practice, when doing REPL-based development, it's very common to use these as opposed to (g(f x)).
Re:The Janet Language by turbohz on Saturday February 18, 2023 @11:24AM (#34848076)
Janet does indeed have those.
Re:The Janet Language by ianthehenry on Saturday February 18, 2023 @11:16AM (#34848018)
Thank you for making an actually relevant point about syntax. I agree with this 100%, and I love Janet, and was recently doing a lot of interactive Janet programming for a generative art playground.

So I added postfix function application. So instead of (f (g x)), you can write (g x | f).

I liked the syntax a lot, but it looked really weird with operators: (calculate x | + 1). So I made operators automatically infix: (calculate x + 1).

I also didn't like that the transformation from foo to (foo :something) (record field access) required going back and adding parentheses before the value, so I added foo.something syntax that means the same thing.

The result is something that's very easy for me to type and read:

    (def eyes
      (eye-shapes
      | color (c + 0.5)
      | color (c - (dot normal (normalize eye-target) - 0.72
              | clamp -1 0
              | step 0))
      | color [c.b c.g c.r]))
(Excerpt from the logo of https://toodle.studio -- https://gist.github.com/ianthehenry/612c980f0db04ea3c2ccab27...)

Is this even Janet anymore? I dunno. It's a Janet dialect, and it's implemented as regular old Janet macros. But it's much easier for me to type like this. I recognize that it makes my code necessarily single-player, but that's fine for the sorts of dumb projects that I do for fun.

I think a lot of lisp programmers use paredit exactly so that they can write (f (g x)) in the order g x f, but with their editor re-positioning their cursor and taking care of paren-wrapping automatically. But I don't use paredit, and I don't want to wed myself to a particular mode of editing anyway. So I like a syntax that lets me type in the order that I think.

Re:The Janet Language by bo-tato on Saturday February 18, 2023 @12:07PM (#34848538)
you're totally right that it's often easier to read in the order the functions are being applied. That's why just about every lisp/scheme family language has thread-first and thread-last macros -> and ->> so f(g(h(x))) could be written as: (-> x h g f) Janet, clojure and racket and probably others have them built in, emacs lisp has it in dash.el, common lisp has it in cl-arrows and other libraries

It's really just about readability preference though not ease of editing, lisp like languages will have paredit/sexp editing shortcuts in your editor, so when you're on (f x), you press one key and it's turned into (<cursor here> (f x))

Re:The Janet Language by camdez on Saturday February 18, 2023 @10:12PM (#34853890)
Emacs Lisp actually has threading macros natively (seemingly since at least 2016), but they're (IMHO) obnoxiously named `thread-first` and `thread-last`.