1. 22
    1. 8

      One that got a little popularity at one time was my Rouge (wayback website). It got a few mentions in books and I was pretty chuffed about it.

      1. 2

        That’s really cool, the syntax for blocks interopt (is that what it is?) looks interesting:

        (.subscribe queue {:ack true} | [metadata payload]
          (puts (str "got a message: " payload))
          (.ack metadata))
        

        Any reason why this couldn’t be just a function passed in? (Since blocks can be procs/lambdas/anything that implements call, sometimes)

        1. 1

          The block form with arguments is syntax sugar that mirrors sugar in Ruby. It’s worth noting that a block isn’t a regular argument in Ruby — it has special treatment in the VM. So in Ruby:

          >> [1, 2, 3].each {|e| puts e}
          1
          2
          3
          => [1, 2, 3]
          >> [1, 2, 3].each(&lambda {|e| puts e})
          1
          2
          3
          => [1, 2, 3]
          >> [1, 2, 3].each(lambda {|e| puts e})
          Traceback (most recent call last):
                  5: from /Users/kivikakk/.rbenv/versions/2.6.3/bin/irb:23:in `<main>'
                  4: from /Users/kivikakk/.rbenv/versions/2.6.3/bin/irb:23:in `load'
                  3: from /Users/kivikakk/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
                  2: from (irb):3
                  1: from (irb):3:in `each'
          ArgumentError (wrong number of arguments (given 1, expected 0))
          >>
          

          Note how the last example fails, because each doesn’t take any positional arguments.

          The first two forms are largely identical, although there is a difference in how the bytecode turns out because blocks get some special treatment in codegen too. (If you’re interested, try puts RubyVM::InstructionSequence.compile('[1,2,3].each {|e| puts e}').disasm, and then again for the &lambda {…} form.)

          Here’s the equivalent in Rouge using Ruby interop:

          user=> (.each [1 2 3] | [e] (puts e))
          1
          2
          3
          [1 2 3]
          user=> (.each [1 2 3] | (fn [e] (puts e)))
          1
          2
          3
          [1 2 3]
          user=> (.each [1 2 3] (fn [e] (puts e)))
          !! ArgumentError: wrong number of arguments (given 1, expected 0)
          /Users/kivikakk/kivikakk/rouge/lib/rouge/context.rb:143:in `each'
          /Users/kivikakk/kivikakk/rouge/lib/rouge/context.rb:143:in `block in eval_symbol'
          /Users/kivikakk/kivikakk/rouge/lib/rouge/context.rb:229:in `eval_call'
          /Users/kivikakk/kivikakk/rouge/lib/rouge/context.rb:187:in `block in eval_cons'
          /Users/kivikakk/kivikakk/rouge/lib/rouge/context.rb:235:in `backtrace_fix'
          /Users/kivikakk/kivikakk/rouge/lib/rouge/context.rb:186:in `eval_cons'
          /Users/kivikakk/kivikakk/rouge/lib/rouge/context.rb:111:in `eval'
          /Users/kivikakk/kivikakk/rouge/lib/rouge/repl.rb:57:in `run!'
          /Users/kivikakk/kivikakk/rouge/lib/rouge.rb:47:in `repl'
          bin/rouge:83:in `<main>'
          user=>
          

          Of course the ~rougey~ way would probably involve an as-yet unwritten doseq implementation. This thing didn’t get too far off the ground in terms of Clojure-compatibility.

          Thanks for responding. It gave me a chance to play around with Rouge, which I haven’t done in a good seven years or so. :)

          1. 2

            Ah true - I forgot about the special handling of blocks. There’s more to it, for example: implicit blocks with yield - it’s been a while since I wrote any Ruby that uses that stuff extensively.

            I wish I had some spare time to play around with it - based on the “seven years” part, it’s not only me ;-)

    2. 4

      The decision to not try to entirely abstract the host away as some other languages do (Java for example, with respect to operating systems) has not in any way hindered the ability of different Clojure implementations to share code between them.

      This seems … patently false? For several years there was no way to conditionally execute code based on which runtime it was running under.

      1. 10

        Yeah, there is a lot of hand-waving in this piece. All implementations of Clojure other than the first-party JVM implementation are distinctly second-class citizens. The latest version of Clojure does not work with Graal–you have to use an older version and many of the most common Clojure libraries are not compatible. Clojurescript works OK in the browser, but most Clojurescript tooling was not designed with Node.js in mind. Code reuse between Clojure and Clojurescript kind of works unless your code needs to do any IO, at which point your Clojurescript becomes a nest of callbacks (and you don’t have access to the async/await stuff that makes async IO tolerable in Javascript).

        I like Clojure and have been using it professionally since 2014, but its strength is definitely in the core language concepts, not the implementation–and certainly not in having multiple compatible implementations, where it lags behind many, many other languages.

        1. 1

          FWIW the Graal issue is being worked on at the moment, slated for release in 1.11.

      2. 4

        I’m sure in those earlier years it was more of a struggle, but I’m speaking from the perspective of someone who started using Clojure in 2017. To me it’s been smooth sailing with .cljc files from the very beginning.

    3. 3

      I dunno, in these times of pandemic panic the title seems a bit off…