Ruby 2.7(ruby-lang.org) |
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?
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...).
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).
Compared to the rest, C99 contains a lot of useful improvements.
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!
Sidenote: I can't think of a use case where this is a good idea.
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.Will have to properly try it out.
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.
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.
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 } list.group_by(&:itself).transform_values(&:count)
...but I'll be very happy to replace that with #tally!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.
Further, the pattern matching feature is significantly different than `map` imo.
The sentence "It can traverse a given object and assign its value if it matches a pattern" sounded like traverse and (optionally) manipulate.
[1, 2, 3].filter_map {|x| x.odd? ? x.to_s : nil } #=> ["1", "3"]
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.
I wasn't aware, thanks for pointing that out.
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.
{ 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>)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.
It’s great in large scale projects, which I think is where a dynamic language starts to show its warts.
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.
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.
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.
> 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.
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!
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.