Settings

Theme

Ruby 2.7

ruby-lang.org

336 points by sadiqmmm 6 years ago · 67 comments

Reader

seanmccann 6 years ago

I had wondered why most recent Ruby releases were on Christmas Day and recently found out why. The Ruby creator Matz is religious (LDS), and he considers it a Christmas gift to the community. I thought that was really cool.

ufo 6 years ago

One of the interesting news is that they are now going to start requiring a C99 compiler, instead of only C90.

I've been considering to do the same on my own projects. What does HN have to say about this? Is anyone here still working in a context where C99 is not an option? Did anyone else also recently switch to C99? How did it go?

  • cesarb 6 years ago

    It's now 2019, and as far as I know, MSVC still doesn't have full support for the 20-year-old C99 standard.

    So either you restrict yourself to the subset of C99 which MSVC understands (AFAIK, newer MSVC releases understand more and more of the C99 standard), or just decide MSVC is no longer relevant (which is easier now that clang-cl exists; some big projects like Firefox and Chrome went this way, see for instance https://blog.mozilla.org/nfroyd/2019/04/25/an-unexpected-ben...).

    • ksec 6 years ago

      I sometimes wonder if someday Microsoft would actively contribute to LLVM.

  • CJefferson 6 years ago

    The only modern problem with switching to C99 is Visual Studio on Windows. You have 2 sensible choices:

    1) Use clang to build on Windows.

    2) Use the subset of C99 which is implemented in Visual Studio (which also requires compiling as C++, which isn't that difficult to handle).

    • gdxhyrd 6 years ago

      3) Use C++17 with minimal features. This gives you a lot of needed libraries built-in and it is better supported than C99 (due to Windows).

      • jhayward 6 years ago

        > Use C++17 with minimal features

        To paraphrase JWZ, "now you have N+M problems"

        • gdxhyrd 6 years ago

          C++17 is a very close language to C18 if you avoid exceptions, classes and templates.

  • brudgers 6 years ago

    Why? Not being facetious. Wondering about the weights of benefits versus costs for users.

    • codr7 6 years ago

      Mostly portable implementations of extensions for me; __thread, bool, inttypes.h, variadic macros, flexible array members etc. But declaring variables closer to the point of use is nice, as is struct initializers, compound literals and single line comments.

      Compared to the rest, C99 contains a lot of useful improvements.

    • kick 6 years ago

      The only real cost is that Windows users will have a slightly less convenient time, which doesn't matter for most projects.

burlesona 6 years ago

> Calling a private method with a literal self as the receiver is now allowed.

Oof. Call me old fashioned, but I liked the consistency of not being able to call private methods with an explicit receiver. Oh well!

The rest of this looks great, thanks Ruby team!

  • atmosx 6 years ago

    Funny thing: I always bring up how Ruby has private methods while Python doesn't, when toying around with colleagues about languages, etc. well there goes that I guess.

    Sidenote: I can't think of a use case where this is a good idea.

    • cheald 6 years ago

      You've been able to use `send` to pierce the protected/private restriction forever, so this doesn't particularly change the nature of Ruby's method visibility rules. Now you can just use `self.foo` and it's the same as `self.send :foo`.

          class Foo
            def test
              priv             # works
              self.send(:priv) # works
              self.priv        # doesn't work under ruby 2.6-
              Foo.new.priv     # doesn't work
            end
      
            private
      
            def priv
              puts "ran private"
            end
          end
      
      You shouldn't typically need to be using `self` at all, except when it's clarifying or disambiguating, so you shouldn't generally run into that issue. On occasion, though, you add a `self.` prefix to a method call and can break code that was otherwise working, because you've subjected your code to a scope protection that it wasn't subject to before.
      • atmosx 6 years ago

        > You've been able to use `send` to pierce the protected/private restriction forever, so this doesn't particularly change the nature of Ruby's method visibility rules.

        I wasn't aware, thanks for pointing that out.

        • Lio 6 years ago

          Previously this was a good reason for preferring public_send over send where possible.

faitswulff 6 years ago

I know it's slow at the moment, but is anyone planning on using pattern matching for anything in particular? Curious what use cases are particularly suitable for it.

  • ljm 6 years ago

    I’d love to, if it helps with things like Either/Maybe types. Although from what I’ve looked at briefly the syntax felt a little less intuitive than I’ve seen in other languages.

    Will have to properly try it out.

    • jolux 6 years ago

      what's the purpose of Either and Maybe in a dynamically typed language?

      • edflsafoiewq 6 years ago

        Pop an item from an array. What do you return when the array is empty?

        Solution: nil. Problem: How do you tell the difference between "the array was empty" and "the item you popped was nil"?

        Solution: have pop return a Maybe instead.

        • alaaalawi 6 years ago

          or allow returning multiple values. for example in CL searching for a thing returns two values the is-found and the value

          • uryga 6 years ago

            ...which, btw, is equivalent to Maybe/Either – they're tagged unions, i.e. pairs (tag, content). Maybe<T> is isomorphic* to

              { has_value: bool,
                val: T|nil }
            
            * well, this representation is a bit too permissive, since you could do

              {has_value: true, val: nil} 
            
            if you wanted to get the types water-tight, you'd need dependent types, typing it as dependent pair:

              Maybe<T> =
                sigma (has_value: bool)
                  if has_value
                    then T
                    else ()
            
            which can then only have values

              (false, ())
            
            or

              (true, <actual value of type T>)
          • coldtea 6 years ago

            Which is the same thing, but in an ad-hoc way, and leaves it open to the developer to check or not, and thus to crash.

        • inopinatus 6 years ago

          or solution: Null Object

          or a whole value

          or use an enumerator

          or a collection decorator

          All of which are simple solutions already available. No need to import every new paradigm hammer in the hope that everything is a nail.

      • ljm 6 years ago

        Maybe I’ve just been bitten by the functional bug but I find they can communicate intent much better than scattering around null checks or catching exceptions (many of which require reading the source to understand what exceptions you might get).

        It’s great in large scale projects, which I think is where a dynamic language starts to show its warts.

        • int_19h 6 years ago

          Given that we're talking about it in a pattern matching context, you'd just pattern match on x vs nil, rather than matching on Just x or None. And in case of Either, you'd match on the types that you expect.

        • obstacle1 6 years ago

          An alternative to scattering null checks around or using exceptions for control flow could be more thoughtfully designing your data/object model. No additional language constructs required!

          • coldtea 6 years ago

            That attitude is the source of billions of dollars in bugs...

            Never manually do the work that the compiler / runtime could do.

            You can design "more thoughtfully designing your data/object model" (in other regards) AND have the compiler make sure you're not doing null referencing for you (so that that's not your concern anymore) -- instead of manually and in an ad-hoc way per project implementing another menial responsibility into the design of your model.

          • ljm 6 years ago

            What if using those functional concepts is my attempt at thoughtful design? You're making a hell of an assumption with that one.

        • jolux 6 years ago

          Yeah I suppose my bigger beef is I just don't like dynamic languages. Perhaps it is in fact a good idea for them, but being someone primarily interested in types, it largely falls into the realm of "don't care."

          • coldtea 6 years ago

            Huh? Both Maybe / Optional and pattern matching is used in Haskell, Scala, and other typed languages... It's not about dynamic languages...

            • jolux 6 years ago

              If you’ll notice my other comments in this thread, I think you’ll see I vastly prefer statically typed functional languages over dynamic ones. I was more commenting that I like monads and functors and Maybe and Either/Result but don't like dynamic languages.

      • chrisseaton 6 years ago

        One use could be that you could capture source information in them, so when you get an unexpected None you can find where it came from.

        • sorokod 6 years ago

          What forces you to check for None?

          • chrisseaton 6 years ago

            The idea of Maybe is that you usually don't check for None. The idea is that you use it sort of like a collection in most cases, allowing the None to propagate up as the result of collection operations on other Nones. The issue can be that you get a None at the end and it isn't clear where it came from originally, and how it reached you. Adding stack trace information to each intermediate None, when in debug mode, can help you trace what went wrong.

            • jolux 6 years ago

              >The idea is that you use it sort of like a collection in most cases

              Sort of like a monad, even!

              Honestly if you're running into problems like that though you should probably be using Either instead of Maybe like GP suggested. In my opinion, most of the benefit is still derived from strong static type checking, because otherwise you basically have to trust that callers respect your contract with these types. Perhaps Ruby 3.0 will make this feasible. I'm not sure of the details of the type system they intend to implement; whether it supports ADTs and such.

              • chrisseaton 6 years ago

                > Sort of like a monad, even!

                Yes that's it - they're monads! Either is also a monad, and you can store error information in the other side if you want, but a good thing about Maybe is it could transparently store the error information, and produce it when debugging, rather than baking it into the normal runtime semantics.

                > you basically have to trust that callers respect your contract with these types

                This is table-stakes for a dynamic language like Ruby, though. Yes people can do anything, but usually they follow the rules they're given.

                • jolux 6 years ago

                  Yes I’m aware they’re monads, my primary languages are OCaml and Haskell. I don’t understand what you’re suggesting with Maybe, that sounds like maybe adding some layer of magic onto it?

                  > Yes people can do anything, but usually they follow the rules they're given.

                  Sure, but the primary advantage remains in not having to trust that, not having to write tests for it, and communicating the contract itself inside the code it restricts. I’m just honestly not sure what purpose they serve here, and why you’d bother to call them monads when what you really seem to want is the stored debugging information.

                  • chrisseaton 6 years ago

                    > I don’t understand what you’re suggesting with Maybe

                    I’m suggesting adding a trace of where a None value is created and propagated, for debugging purposes.

                    > the primary advantage remains in not having to trust that, not having to write tests for it

                    Is that the primary advantage? I’m not as sure as you seem to be.

                    And of course there’s one huge part of monads that you have to take on trust even in Haskell isn’t there? The monad laws! No type system is enforcing those. If you’re comfortable trusting someone else is following that rule why is trusting they follow another so alien?

                    But if you don’t like the idea don’t worry I’m not going to come round to your house and force you!

                    • jolux 6 years ago

                      >Is that the primary advantage? I’m not as sure as you seem to be.

                      It's the combination of that and exhaustive pattern matching that I see as the reason to use strongly-typed functional languages.

                      >No type system is enforcing those.

                      This is quite different from not having static type checks at all though. At least with static typing I know that bind and fmap have the correct signature, which is certainly not a verification that they follow _all_ the rules they should (though I am a big fan of dependently typed languages too), yet it is still assurance that the function fulfills a basic premise.

                      >If you’re comfortable trusting someone else is following that rule why is trusting they follow another so alien?

                      Because I have seem them betray that trust, over and over again, intentionally and unintentionally, though never actively malicious. Type checking is work the compiler can do for me, so I see no reason to do it myself, where I am known to be rather clumsy and stupid and prone to writing bugs and being human.

      • StuffedParrot 6 years ago

        How are they related at all?

sergiotapia 6 years ago

Pattern matching is awesome in Elixir, it's great to see functional language traits stain the Ruby language more. It's all the better for it.

  • papito 6 years ago

    I was introduced to it in Scala. I like how all "classic" languages are cherry-picking features from functional programming. It will make people less shell-shocked in the future, when they try it for real.

    • nobleach 6 years ago

      Yup, Scala, F#/OCaml and Rust have the best pattern matching implementations I've seen. I'm happy to see other languages slowly coming around. Java has a JEP for it (although nowhere near as nice as Scala's). Even JavaScript has a TC-39 proposal, although its champion has gone on to focus more on Rust. I'm excited for the future.

  • pmontra 6 years ago

    Basically this is Elixir's case, so a very narrow instance of what Elixir's pattern matching is. I'd love to see more pattern matching added to Ruby year after year. However adding everything Elixir has probably would require a total rework of the language. Example: multiple method definitions, one per pattern.

    I wonder what's going to be the idiomatic way to handle a failed match. Elixir ends the process but there is likely a supervisor to restart it. In the case of Ruby the process ends and there is nothing to restart it. In the case of Rails I expect the web request to fail with a 500 and maybe the next one will get more luck.

    • riffraff 6 years ago

      There seem to be plans to extend this to destructuring assignment/bind, that would be a ton more useful and interesting imo.

  • omneity 6 years ago

    My first exposure to it was in Haskell. It felt very surreal and almost magical at the time.

z92 6 years ago

The compaction GC was the most important change. I haven't measured Ruby's performance specifically but in general the difference and improvements over a non-compacting GC is so huge. Specially for long running processes.

  • chucke 6 years ago

    Its an important change, but not huge, at least not yet: it's not yet integrated in the GC, which means you have to explicitly request it. And most of the fragmentation comes anyway from the allocations heap. That being said, I think this will improve in the coming releases.

    • ultrarunner 6 years ago

      What criteria should be monitored to request compaction in a long-running process? Is a timer sufficient (at what interval?) or are there other tools available in Ruby to make that decision?

      • brandur 6 years ago

        It's likely to be most useful for anyone using a Unicorn-style process forking model in Ruby.

        You call `GC.compact` in the parent right before forking off your child processes and because the memory in the children are copy-on-write (COW), it lets them share memory with their parent far longer than they normally would be able to.

        Any change in a page (i.e. an object allocated or deallocated) causes it to be copied to a child process, and because previously pages were a mix of objects of all kinds of longevity and slots which may be empty or used, children tended to copy their parent's entire memory space very quickly. Running a GC and compact before forks improves the likelihood that shared pages are mostly full of still-in-use, longer-lived objects, and gives the COW strategy a fighting chance.

  • ksec 6 years ago

    >over a non-compacting GC is so huge

    Yes in other languages, but as far as I am aware that is not the case in Ruby ( Specific to Rails ) . At least I wouldn't use so huge to describe the improvement.

mattmarcus 6 years ago

I'm most excited for Enumerable#tally for counting occurrences of elements.

From the example in the release:

  ["a", "b", "c", "b"].tally
  #=> {"a"=>1, "b"=>2, "c"=>1}
There's more about this change here (https://medium.com/@baweaver/ruby-2-7-enumerable-tally-a706a...). I probably do this a few times a week:

  list.each_with_object(Hash.new(0)) { |v, h| h[v.something] += 1 }
  • meowface 6 years ago

    For Python developers: this exists as collections.Counter.

    • Twirrim 6 years ago

      I stumbled across collections.Counter during last year's Advent of Code, and promptly kicked myself for the number of times I'd implmeented that by hand.

  • faitswulff 6 years ago

    I was a fan of

        list.group_by(&:itself).transform_values(&:count)
    
    ...but I'll be very happy to replace that with #tally!
tyingq 6 years ago

Does the pattern matching do something that Ruby's map() can't do?

Coming from Perl, map() there can return fewer elements than the source list, so pattern matching works already. A short skim of Ruby's map seems to imply it always returns something with the same number of elements.

Edit: I was confused about what this feature did. So this subthread is still interesting, but mostly unrelated.

  • rubyfan 6 years ago

    Yeah afaik something is always returned from the block (even if it’s nil). Occasionally I recall my self doing something where the block might return nil and then I’d call `compact` to get rid of extra nil stuff. It works when you want a modified version of the enumerable but don’t want nil stuff. If you just want to match a subset then I think `select` is probably what you want.

    Further, the pattern matching feature is significantly different than `map` imo.

    • knrz 6 years ago

      In JavaScript, Elixir, and Ruby I use flatMap for this purpose. You can return an empty array on the block to “skip” over the element.

      • Rafert 6 years ago

        2.7 introduces `Enumerable#filter_map` for that. Example from the NEWS file:

        [1, 2, 3].filter_map {|x| x.odd? ? x.to_s : nil } #=> ["1", "3"]

  • s_kilk 6 years ago

    Map is data manipulation, whereas pattern-matching is control flow.

    • tyingq 6 years ago

      Yes, thank you. Reading the sub linked presentation, I see it now.

      The sentence "It can traverse a given object and assign its value if it matches a pattern" sounded like traverse and (optionally) manipulate.

claudiug 6 years ago

great! i truly like this language.

atraktor 6 years ago

Love Matz & Ruby, Merry Christmas!

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection