Threads for jeremy

  1. 2

    This is an announcement to a list of articles about ruby, currently one: https://timriley.info/writing/2022/03/24/let-the-shape-of-the-code-reflect-its-flow/

    I would rather do Message.new(message).tap(&:decode).process or decode in the initializer, not sure

    1. 2

      In Clojure, the thread-first macro would be used instead of a chain calls in a readable way.

      (-> message decode build-event process)
      

      I’m not familiar with Ruby, but I’m guessing the chain of then calls can’t be collapsed?

      message.then(decode).then(build_event).then(process)
      

      I understand the author’s concern that not naming the intermediate values can make understanding the intermediate types unclear, but I think not having the names increases readability. In most cases, the intermediate structures are defined by or near the function definitions that use them.

      1. 1

        Unfortunately, Ruby doesn’t have first class support for functions - decode would be interpreted the same as decode() and would throw an ArgumentError. You would have to do something like message.then(&method(:decode)).then(&method(:process)).

        1. 7

          I’ve seen people say this a lot, that Ruby doesn’t have first-class functions, but I think that is wrong. It’s just not syntactically as convenient as other languages, but you absolutely can pass methods around as objects.

          operation = method(:decode) >> method(:build_event) >> method(:process)
          operation.(message)
          

          Method objects and procs/lambdas are interchangeable here.

          1. 1

            Here’s my response to whether Ruby has first-class functions: https://briankung.dev/2022/03/27/ruby-has-economy-class-functions/

            1. 1

              I disagree with your argument because the entire Higher-Order Functions section confuses the difference between methods and functions. All of the problems you demonstrate come from that misunderstanding.

              Next, let’s try to return functions from functions in Ruby:

              def returns_function
                def addition(a, b)
                  a + b
                end
              end
              

              You are not instantiating a function here at all, you are metaprogramming the receiver of returns_function to add a new method. There are times where you’d actually want to do this, but they have nothing to do with higher-order functions.

              add = returns_function
              #=> :addition
              add(1,2)
              (irb):10:in `<main>': undefined method `add' for main:Object (NoMethodError)
              

              You can do this pretty handily in Javascript:

              let returnsFunction = function() {
                return function(a,b) {return a + b };
              }
              let addition = returnsFunction();
              addition(1,2);
              // 3
              

              Yes, doing two entirely different things behaves differently! Allow me to flip this comparison on its head:

              dog = Object.new
              def dog.bark = "woof!"
              dog.bark # => "woof!"
              
              function addBark (obj) {
                obj.bark = function() { return "woof!" }
              }
              
              let dog = {};
              let bark = addBark(dog);
              
              bark() // Uncaught TypeError: bark is not a function
              

              Huh, JavaScript sucks at methods doesn’t it? (No, this was intentionally wrong)

              An apples-to-apples comparison looks a lot closer:

              Currying in Ruby

              addition = ->(x, y) { x + y }
              add_two => addition.curry.(2)
              add_two.(4) # => 6
              

              Currying in JS

              let addition = (x, y) => x + y
              let addTwo = (y) => addition(2, y)
              addTwo(4) // 6
              

              Ruby makes different tradeoffs between methods and functions than JavaScript or other languages. JS is more comfortable for pure functions, but methods are janky. Ruby puts most of its ergonomic focus on methods, with implicit block arguments fullfilling the majority of first-class function use-cases, and native procs and lambdas filling in the blanks.

              This leads to more complexity in some cases, it’s true. But some of the interactions between them is much richer than JS APIs can provide. For instance:

              require 'markaby'
              
              mab = Markaby::Builder.new
              mab.html do
                head { title "Boats.com" }
                body do
                  h1 "Boats.com has great deals"
                  ul do
                    li "$49 for a canoe"
                    li "$39 for a raft"
                    li "$29 for a huge boot that floats and can fit 5 people"
                  end
                end
              end
              
              1. 1

                I think you’re 1) making a strawman argument, and 2) ignoring the ergonomics of what first-class functions means. The fact that you have to think about any of this at all in order to use Ruby’s “first class functions” means you can only say it has them with a lot of disclaimers. As I put it in the post, “economy-class.” Note that I don’t deny that it technically has them!

                JS is more comfortable for pure functions, but methods are janky. Ruby puts most of its ergonomic focus on methods

                To be equally pedantic, we weren’t talking about methods, were we? We’re talking about first-class functions. Not first-class methods. Yes, the syntax you call out defines a method on the caller, but do programmers used to first-class functions want to think about that? No, they do not. In fact, the fact that the def keyword defines methods and not functions makes it even more clear that Ruby does not treat functions as first-class.

                Yes, you can curry in Ruby. But it’s an incredibly uncommon pattern. Why? Because Ruby doesn’t treat functions as truly first class. You can do it - that was the whole point of my post - but it’s awkward. Wikipedia labels Ruby’s first-class functions support with the equivalent of asterisks:

                • The identifier of a regular “function” in Ruby (which is really a method) cannot be used as a value or passed. It must first be retrieved into a Method or Proc object to be used as first-class data. The syntax for calling such a function object differs from calling regular methods.
                • Nested method definitions do not actually nest the scope.
                • Explicit currying with Proc#curry.

                Also yes, you can do cool stuff with instance_eval, but that’s not what we’re talking about. We’re talking about language-level support for first-class functions.

                1. 0

                  I don’t understand how that could possibly be construed as a strawman, the entirety of my comments were responding to the substance of your blog post. Unless you are referring to the JS example, which I pointed out directly was intentionally wrong to make a point.

                  The fact that you have to think about any of this at all in order to use Ruby’s “first class functions” means you can only say it has them with a lot of disclaimers. As I put it in the post, “economy-class.” […] To be equally pedantic, we weren’t talking about methods, were we? We’re talking about first-class functions.

                  Ruby has first-class functions without any disclaimers. My critique of your argument is that you keep confusing method invocation for function execution and these are two entirely different language concepts.

                  To be explicitly clear: when I say Ruby has first-class functions, I am speaking of Procs and Lambdas. Those are functions. Methods are not. Two different things.

                  1. Assigning a function to a variable
                  greet = ->(name) { "Hello, #{name}!" }
                  greet.("John") # => "Hello, John!"
                  
                  1. Passing a function as an argument
                  transform = ->(str, fn) { fn.(str) }
                  transform.("foo", -> { _1.reverse }) # => "oof"
                  

                  It’s also worth mentioning that your example:

                  array.select {|number| number.even? }
                  

                  You are demonstrating this property, which can also be written as

                  evens = proc { |number| number.even? }
                  array.send :select, &evens
                  

                  Block arguments are Procs that are passed as arguments to methods. This is the most common way of using first-class functions in Ruby, and it’s one of the most unique language features.

                  1. Returned as a result of a function
                  curry = ->(fn, x) { ->(*args) { fn.(x, *args) } }
                  addition = ->(x, y) { x + y }
                  add_one = curry.(addition, 1)
                  add_one.(1) # => 2
                  
                  1. Including a function in any data structure
                  transforms = {
                    reverse: -> { _1.reverse },
                    upcase: -> { _1.upcase }
                  }
                  transforms[:reverse].("foo") # => "oof"
                  transforms[:upcase].("foo") # => "FOO"
                  
                  1. Composing multiple functions together
                  reverse = -> { _1.reverse }
                  upcase = -> { _1.upcase }
                  shout_in_reverse = reverse >> upcase
                  shout_in_reverse.("foo") # => "OOF"
                  
                  1. Functions are closures

                  This isn’t strictly necessary to qualify as first-class, but I think it is the most common way to do it.

                  count = 0
                  incr = -> { count += 1 }
                  incr.() # => 1
                  count # => 1
                  

                  Is that more clear? Functions in Ruby are invoked with call which has syntactic sugar .().

                  Second, methods are not functions.

                  1. Methods are not closures
                  count = 0
                  def incr = count += 1
                  incr() # NoMethodError: undefined method `+' for nil:NilClass
                  

                  This happened because the execution binding of a method is the receiver, not the lexical scope. This is important because calling a method is essentially just syntactic sugar for send

                  This can be confusing in a REPL specifically, because it’s not clear from the above what you are actually doing. In a Ruby REPL, main is an instance of Object, and calling def is actually defining a new method on the Object class.

                  1. Method invocation is just send
                  dog = Object.new
                  def dog.bark = "woof!"
                  dog.bark # => "woof!"
                  dog.send :bark # => "woof!"
                  

                  Method objects exist so that you can translate the method interface into a function interface, but you’re really just bundling up the receiver together with a message to send

                  bark = dog.method(:bark)
                  bark.() # => "woof!"
                  
                  1. Methods can’t be detached from their receiver

                  A method is a type of message that an object can respond to. You can’t separate them (well, you can but UnboundMethod is not executable until it is bound to something)

                  A Ruby method is an implementation of the OOP idea of data and behavior existing together in a single object.

                  All methods have an implicit block argument (a Proc), which is a first-class function. The syntax of Ruby allows you to seamlessly combine methods and functions into a single interface, but there is no requirement that you do so.

    1. 13

      I agree with part of the premise, but this is way, way overengineered. My main problem with logs is they can clutter the code, making the code itself harder to understand at a glance. For that, having a simple function that abstracts the log into a nice compact name, something like LogExternalDependencyResponse() solves all of my problems.

      Why would I introduce all this machinery for logging? To decouple logging code from application logic? Logging code is one of the most clear examples of application logic.

      1. 15

        Isn’t it a Java principle that any problem can be solved by adding more interfaces to the program, and requiring more rituals around everyday tasks?

        1. 2

          The machinery needed to do this can be scaled up and down to your needs. For example, all events can be written to a file as JSON, where another process can be used to subscribe to the events. This key difference between events and logging is that each kind of event has a name and possibly some structure, whereas a log is typically unstructured text.

          1. 1

            What is another use for this?

            1. 3

              Another use for this is to do any sort of async processing based on events. For example, if you have an InviteCreatedEvent, then you can have something else listening for that event that sends out the invite email. The point is that you can get more out of your logs by pulling them into the application layer a tiny bit.

              1. 2

                Unit testing. Verifying that a particular log message has been written to the system log is cumbersome (in Java). Using events makes validating the application behaviour trivial: execute the production code under test and block until the event is received. Presuming that the event machinery is solid, there’s no need to add any extra code to scan the system log file for updates.

                1. 1

                  Not at all cumbersome if you use something like SLF4J-test as noted in my article https://www.jvt.me/posts/2019/09/22/testing-slf4j-logs/

                  1. 1

                    That’s neat, thank you for sharing. An advantage of the event method remains checking types and avoiding checking log message text except when validating the actual text of the message, which would be a test of the string representation or specific message owned by the event. Looking at the log output is one way to do it but that’s always felt brittle to me, esp. in a high change velocity codebase with multiple contributors having their own opinions of how things should be worded. At least the tests would catch that someone hasn’t deleted a log emission! slf4j-testing seems like a much cleaner way to look for log output than capturing stdout yourself, though!

          1. 3

            I enjoy dark mode when it comes to UI chrome, but I find it hard to read in many cases.

            How do I switch it back without changing my OS setting?

            1. 1
              1. Complain to your browser/OS for assuming that you want a consistent theme.
              2. There has been talk that an override was out of scope for the original feature but they are not opposed to adding it.
            1. 2

              I’m continuing to read “Threaded Interpretive Languages”. The author builds a language using the Z80 instruction set, and my current thought is “can I build one using the JVM as my machine?”

              1. 1

                I am working on my Joy-like programming language Rejoice.

                1. 3

                  I’m going to continue working through Crafting Interpreters, and I’m reintroducing myself to Pony.

                  1. 4

                    I get tired and drained: As a bit of an introvert, I find extended periods of pairing extremely draining. I don’t think introversion is really factored in when the XP crew advocate for full-time pair programming.

                    This is my experience as well. To exacerbate the point, I’ve found that some people believe that being introverted is a problem, and a behavior that needs to be “corrected”. I don’t know how recent the idea of introversion vs extroversion being expressed in terms of when your energy is being drained came about, but it seems more known today. (And to reiterate, this doesn’t mean introverts are naturally anti-collaboration. This just means other forms of collaboration need to be considered to be mutually beneficial for various personality types.)

                      1. 4

                        I’m working my way through Think Stats.

                        1. 4

                          Nice!

                          I was just digging into this myself: https://github.com/jeremyheiler/regex

                          1. 33

                            What kind of bothers me about this series of back and forths is so many of them are about syntax stuff and not about semantics. C has a lot of semantics we shouldn’t copy, the syntax is a less interesting problem, IMO.

                            1. 14

                              I agree this seems pretty petty. For me the reason to be militantly anti-C is that the current culture of unsafe languages for high performance work is what is giving governments and organized crime the keys to every single one of our servers, cars, toasters, web browsers, voting machines etc… We have an obligation as engineers to build things that are not going to turn into weapons against the people who trust us to build the infrastructure that they depend on. Governments are starting to push their limits and nation states are dramatically escalating their malicious use of our machines. Exploits don’t get fixed, they get sold and hoarded and used against activists. It’s reprehensible that we are standing by with our hands in the air saying “I can write safe C, aren’t I macho?” while our infrastructure is being weaponized at a rapidly increasing rate. The security industry is about stunt hacking, feeling like you’re an early 90s hacker, and churning out snake oil as fast as possible. I can’t wait for the eventual fruits of the cyber grand challenge and languages like rust to drive that vacuous industry out of the technology space after they have abused our paranoia for so much blood money with vanishingly few real contributions to our security. They don’t make technology, they make magic totems against boogeymen that don’t work and can’t work. As engineers we significantly shape the armatures upon which politics has to operate within, and we’re making the world a shittier place by not fighting for memory safety in high performance widely deployed infrastructure.

                              1. 7

                                to be fair; the author of the original post is mostly concerned with language usability and ergonomics, thus syntax is actually of some importance.

                                this is not to say that you’re wrong, C sins badly in both domains, but I don’t think that a focus on syntax is inappropriate, given Eevee’s concerns.

                                1. 3

                                  The author of the original post has been thinking about making a language, in my understanding, and so musing about syntax is very useful.

                                  1. 4

                                    the syntax is a less interesting problem

                                    I want to agree with this on principle, but in practice a bad syntax can make the editing experience suck. I’m pretty fed up with haskell-mode’s poor indentation support, and I hate how in Python you can’t copy/paste code without fiddling with the indentation. And in every non-Lispy language I miss structural editing a-la paredit.

                                    I think as long as the syntax is LL(1) the differences are mostly personal preference.

                                    1. 2

                                      I’ve always thought we should leave the syntax flexible on a per-user basis while standardizing on the semantics. There could be a default syntax that people can understand in general. The main idea, though, is the syntax is a skin on the semantics like the skins on media players, browsers, etc. They’re pluggable in the editors where the language looks however you wanted it to no matter whose library you used. Some semantics might even be converted if it’s something simple to make an equivalent for. Recursion to iteration is potentially one of those.

                                      1. 1

                                        So, syntax macros?

                                        1. 1

                                          It can be that simple. Otherwise IDE support implemented however they choose.

                                    1. 2

                                      It would be very neat if the author included links to libraries that implemented this model of regex.

                                      1. 4

                                        The author’s library re2 uses this technique.

                                        1. 3

                                          And so does Go’s regexp library from an overlapping set of the same authors

                                          1. 2

                                            Rust’s implementation is also based on RE2: https://doc.rust-lang.org/regex/regex/index.html

                                      1. 4

                                        I’m working on improving ponylang-mode, an Emacs major mode for Pony.

                                        1. 2

                                          As someone who is interested in languages and compilers, this writeup has inspired me to consider JavaScript-related projects as something I would work on. I’ve always known that there is a wealth of interesting stuff happening, but my lack of interest in front-end development held me back.

                                          1. 2

                                            I plan on attending. I went last year, and I agree; It was a great conference.

                                            1. 5

                                              What about the costs of SSL certificates? This isn’t going to bode well, especially for those who want to use more than one level of subdomains.

                                              1. 7

                                                The was a link in the comments to Lets Encrypt a project to provide free, automated, Certificate Authority - coming some time this year…

                                                1. 6

                                                  For how many years now have people been talking about a free, non-profit CA? It’s easy to make claims about how simple and automatic this will be when it doesn’t exist yet. It seems like jumping the gun to make this deprecation decision based on the predicted future availability of something that might or might not actually materialize and might or might not be as useful and well-implemented as promised.

                                                  1. 3

                                                    I would bet that Mozilla know at least a little about the CA jobs and if someone can do it, there is a lot of chance that them will do. Moreover we will see how it will be implemented just in a few month ahead.

                                              1. 3

                                                Basic Auth lacks a time nounce to prevent replay attacks, but the alternative is not OAuth which is more complicated as the problem it solves is Aut{oriz,henticat}ing I’m user A in C to B without sharing credentials

                                                1. 4

                                                  Other than not being standardized, is there something that prevents an API from requiring an extra nonce header?

                                                  1. 0

                                                    On the weekend I’m often seen driving around in my Oriz Henticat, it’s a V8.

                                                  1. 9

                                                    I’m all for using established solutions and not re-inventing the wheel, but I think it’s solving a different problem than OAuth set out to. What if I want to write a REST API to be used by untrusted clients? I’m not shoving a secret into my client side JS app.

                                                    Also last I checked Basic Auth has no good standard way to un-authenticate.

                                                    1. 4

                                                      Completely true, but - imo - oauth shouldn’t be considered the default for api authentication.

                                                      You’re fully right about unauthenticating; only way known to me is the hackish unauthentication with wrong credentials.

                                                      1. 5

                                                        What do you mean by “unauthenticate”? I am under the impression that you have to pass the credentials with every request. Or are you talking about how browsers cache the credentials for you?

                                                        1. 3

                                                          The latter; since it’s basically a stateless affair; should’ve been more clear on this.

                                                          Would be nice if browsers would offer a built in logout button.

                                                          1. 2

                                                            I agree. Though, do browsers cache basic auth credentials when making API calls from JavaScript? If not, I feel like this is not a general issue, but a UX one.

                                                            1. 2

                                                              Afaik it is purely a ux issue: js initiated requests require the authorization header on every request.

                                                      2. 1

                                                        You don’t have to shove your secret in the client side, but you could shove your public key.

                                                      1. 2

                                                        This was the first paper we read in our private reading group that was the precursor to Papers We Love. I highly recommend it.

                                                        1. 13

                                                          I’m not sure I like putting the nav at the top like that. It means every time I read a page, the first thing I have to do is page forward. It also seems to put the nav more “out of reach” when I get to the end and want to go somewhere else. Sure, I usually have to scroll with the side nav anyway, but it feels like I’m scanning within the side nav; it doesn’t feel like I’m leaping to another part of the page. When it’s not on the page, it feels more detached.

                                                          1. 5

                                                            In that particular example of documentation too, it seems very likely that one would want to be jumping around a lot and hence having an always-visible ToC would be useful.

                                                            1. 2

                                                              As much as I’ve been hating on floating top bars lately, having something simple with a drop down for the nav might be nice in that situation.

                                                              1. 8

                                                                The ginormous top bars which then try to fold up into smaller versions have a tendency to break, among other obnoxious behaviors. But I’ve rarely been bothered by fixed size absolute divs that float above the page.

                                                                The folding header seems like a sad skeumorphic newspaper headline. Interfaces that simply float over content seem much more futuristic.

                                                                Perhaps nothing is worse than janky sidebars which use JavaScript to reposition themselves, but never in sync with scrolling.