11 ms·
My Weird Ruby
- jrochkind1 12y agoIs there an ETA for middleman 4.0?
- grandalf 12y agoThat Ruby looks excellent. I hadn't realized the contracts gem existed. So much of what people redundantly cram into test suites could be handled with simple contracts.
- Skoofoo 12y agoRuby is duck-typed, so it is advantageous to write code and tests that are not tied to classes at all. http://www.poodr.com/ http://www.poodr.com/ goes in-depth about this.
- grandalf 12y agoWell, the same kind of contracts approach could be used to enforce a duck-typed set of behaviors: [:quacks], [:barks] => Maybe[:flies] Some Rubyists get pedantic about duck typing. It's just a tool to design good systems, not an article of faith.
- masklinn 12y agoYeah the contract here is a form of nominative type checking, it could just as well be structural.
- bshimmin 12y agoIf, like me, you were wondering how contracts.ruby works, the comments in here are quite instructive: https://github.com/egonSchiele/contracts.ruby/blob/master/lib/contracts/decorators.rb https://github.com/egonSchiele/contracts.ruby/blob/master/li...
- grandalf 12y agovery cool.
- unknownian 12y agoIs Rubinius X still a thing?
- rubyn00bie 12y agoSome small but I think important nit-picky things about Struct 1.) A struct in ruby isn't just data, it is an object. 2.) Structs come with some weird gotchas, most notably that it is an Enumerable! 3.) It's generally better to use a hash (in ruby) if you want "pure" data object (and they at least used to be faster). I myself have written a lot of "weird" Ruby and I will say that more often than not it's detrimental to an application that is used/developed by others. Often times ideas that seem brilliant in one language, cannot be translated into another (immutability), and it's best to use the recommended paradigms. Especially when those using the software are not familiar with them, and most likely won't become familiar with them.... For me, many functional paradigms are just lost on ruby because it's so highly mutable that practicing them is more academic than practical (sadly). That's why I write Scala or Elixir now when I want/need those paradigms-- because they're the right tools for that job. I definitely don't want to discourage innovation, or bringing great ideas to ruby from other places-- I just want to emphasize caution :)
- grandalf 12y agoYou can always freeze your hashes in Ruby for safety if you want to. All that's missing is some sugar for declaring immutable hash literals. I guess one way would be to allow clojure-style minimal punctuation map literals and make them immutable...
- masklinn 12y ago> ou can always freeze your hashes in Ruby for safety if you want to. All that's missing is some sugar for declaring immutable hash literals. And immutable structures with efficient "updates". Because otherwise every time you're altering the structure you have to copy it in full (though shallowly) then mutate it in place.
- grandalf 12y agoWell you could use this: https://github.com/hamstergem/hamster https://github.com/hamstergem/hamster
- 12y ago
- phamilton 12y agoI'd be really interested to see success typing applied to ruby. http://user.it.uu.se/~kostis/Papers/succ_types.pdf http://user.it.uu.se/~kostis/Papers/succ_types.pdf TL/DR: Success typing considers all possible types a value can have. If a function returns either a bool or an int and you then pass that value into a function that accepts a string or an int, then the success typing checks out. It doesn't mean your program is correct, but that it could possibly be correct. On the other hand, if you pass the "bool or int" value into a function that only accepts a string, the type checker will complain and you know for sure your program is incorrect. In other words, you will get false negatives but never a false positive.
- marvy 12y agoBut how do you know what the function only accepts a string in a dynamic, duck-typed language?
- phamilton 12y agoThe same way the contracts gem does it. Annotations.
- Smfranz 12y agoRuby is not a duck-typed language. "duck-typing" is only touted by its community to award it free credibility and merit where there is none. "duck-typing" is essentially free for any dynamic typed language which have method RTTI. So please, stop the "duck-typing" pride parade. It is nauseating.
- marvy 12y agoHuh? If Ruby is not duck-typed, can you give me an example of a something that is? It seems like the textbook example. Here are two weird examples: 1. C++ templates and 2) C# variables declared dynamic (I don't know C#, could be wrong) Are we using the same definitions as each other?
- phamilton 12y ago
- lancefisher 12y agoWhy kill symbols?
- bigtunacan 12y agoI was wondering this as well. Symbols are just atoms and atoms are great. The biggest issue with symbols in ruby is how gc is handled and that has improved in recent releases.
- TylerJay 12y agothirded. Can someone who doesn't like symbols help me understand the downsides of them? I really like how natural it makes passing around params that can be one of {x1,x2,x3...,xn} different values. Accomplishing the same with strings just feels messier and more prone to error.
- bigtunacan 12y agoThe only problem with Ruby's implementation of symbols was possibility of DoS which is resolved through garbage collection in the latest Ruby. Something that I found a bit ironic about the video linked in the article is at 5 mins 30 secs in; he states, "Why not give the memory address a name that makes sense to a person?" Here is referring to assembly and the abstraction it makes and how this relates to the abstraction that is variables. Symbols are really a cross system immutable memory address abstraction; I don't see how this is a bad thing.
- bigtunacan 12y agoSee djur's response to me in the thread. Apparently the cross processes piece is not true. So now that symbols are garbage collected there and the way "string".freeze works now the two are now basically the same construct.
- sferik 12y ago> Can someone who doesn't like symbols help me understand the downsides of them? I wish I had been clearer in my talk but I only had 30 minutes and wanted to cover other topics. Here is a more comprehensive argument against symbols in Ruby: In every instance where you use a literal symbol in your Ruby source code, you could you could replace it with the equivalent string (i.e. calling Symbol#to_s on it) without changing the semantics of your program. Symbols exist purely as a performance optimization. Specifically, the optimization is: instead of allocating new memory every time a literal string is used, lookup that symbol in a hash table, which can be done in constant time. There is also a memory savings from not having to re-allocate memory for existing symbols. As of Ruby 2.1.0, both of these benefits are redundant. You can get the same performance benefits by using frozen strings instead of symbols. "string".freeze.object_id == "string".freeze.object_id Since this is now true, symbols have become a vestigial type. Their main function is maintaining backward compatibility with existing code. Here is a short benchmark: def measure t0 = Time.now yield t1 = Time.now return t1 - t0 end N = 1_000_000 puts measure { N.times { "string" } } puts measure { N.times { "string".freeze } } puts measure { N.times { :symbol } } There are a few things to take away from this benchmark: 1. Symbols and frozen strings offer identical performance, as I claim above. 2. Allocating a million strings takes about twice as long as allocating one string, putting it in into a hash table, and looking it up a million times. 3. You can allocate a million strings on your 2015 computer in about a tenth of a second. If you’ve optimized your code to the point where string allocation is your bottleneck and you still need it to run faster, you probably shouldn’t be using Ruby. With respect to memory consumption, at the time when Matz began working on Ruby, most laptops had 8 megabytes of memory. Today, I am typing this on a laptop with 8 gigabytes. Servers have terabytes. I’m not arguing that we shouldn’t be worried about memory consumption. I’m just pointing out that it is literally 1,000 times less important that it was when Ruby was designed. Ruby was designed to be a high-level language, meaning that the programmer should be able to think about the program in human terms and not have to think about low-level computer concerns, like managing memory. This is why Ruby has a garbage collector. It trades off some memory efficiency and performance to make it easier for the programmer. New programmers don’t need to understand or perform memory management. They don’t need to know what memory is. They don’t even need to know that the garbage collector exists (let alone what it does or how it does it). This makes the language much easier to learn and allows programmers to be more productive, faster. Symbols require the programmer to understand and think about memory all the time. This adds conceptual overhead, making the language harder to learn, and forcing programmers to make the following decision over and over again: Should I use a symbol or a string? The answer to this question is almost certainly inconsequential but, in the aggregate, it has consumed hours upon hours of my (and your) valuable time. This has culminated in objects like Hashie, ActiveSupport’s HashWithIndifferentAccess, and extlib’s Mash, which exist to abstract away the difference between symbols and strings. If you search GitHub for "def stringify_keys" or "def symbolize_keys", you will find over 15,000 Ruby implementations (or copies) of these methods to convert back and forth between symbols and strings. Why? Because the vast majority of the time it doesn’t matter. Programmers just want to consistently use one or the other. Beyond questions of language design, symbols aren’t merely a harmless, vestigial appendage to Ruby. They have been a denial of service attack vector (e.g. CVE-2014-0082), since they weren’t garbage collected until Ruby 2.2. Now that they are garbage collected, their behavior is even closer to a frozen string. So, tell me: Why do we need symbols, again? I should mention, I’d be okay with :foo being syntactic sugar for a frozen string, as long as :foo == "foo" is true. This would go a long way toward making existing code backward compatible (of course, this would cause some other code to break, so—like everything—it’s a tradeoff).
- wasd 12y agoTraveling Ruby is a good way to package ruby applications. https://github.com/phusion/traveling-ruby https://github.com/phusion/traveling-ruby
- MrBra 12y agobut you can't yet build Windows binaries ON Windows
- dkarapetyan 12y agoThat's not that weird. After trying to learn Haskell and reading "Understanding Computation" the Struct and enumerable style of structuring code feels very natural. I disagree with the conclusions though. Contracts and replacing `nil` with `Maybe` doesn't sound right to me at all since I use `nil` pretty heavily all over the place and the extra layer of `Maybe` doesn't buy me anything. I don't understand what he means by a dependency system since Gems have their dependencies spelled out pretty explicitly. Killing symbols is just silly because I use them to tag all sorts of stuff and message passing with symbols feels more natural than any other data type. Packaging could be better and optional types would also be nice. I like how TypeScript does it actually quite a lot.
- codygman 12y ago> extra layer of `Maybe` doesn't buy me anything. Wouldn't that layer be there all the time anyway? Either in the form of a sum type (Just a | Nothing), explicit nil checking, or ignoring nil's and getting a runtime error.
- dkarapetyan 12y agoIt's there with nil to begin with. A Maybe type without pattern matching is basically nil checking. Now if he had said I want some kind of pattern matching then I would get behind that.
- codygman 12y agoSo you would prefer a language without sum types but with pattern matching and exhaustiveness checking for nil then?
- dkarapetyan 12y agoNot what I said and I don't see why you make those choices mutually exclusive. You can have pattern matching without sum types and you can have sum types without pattern matching. In fact Ruby's case/when statement is very limited form of pattern matching that can be put to great use with proper use of Structs and overloading of ===.
- jfaucett 12y agoI'd also add that rubys confusion about what a function is is the thing that's been annoying me the most lately. You have procs, lambdas, blocks, and all with subtle differences (http://awaxman11.github.io/blog/2013/08/05/what-is-the-difference-between-a-block/ http://awaxman11.github.io/blog/2013/08/05/what-is-the-diffe...), and all I want are simple funcs as first class citizens that have consistent behavior. (TCO as a default would also be nice). "And finally, pie in the sky, can we solve packaging apps up into distributable binaries, please? Rust and Go are making us look bad." As a ruby programmer the lack of any reasonable - i.e. not hacky - way to build binaries is annoying. Unless you're building a server side app, you can pretty much forget using ruby because of this. Maybe that's all ruby cares about, its certainly its niche but I like ruby and would like to be able to use it for cli programs, etc. Personally, I don't want end users of my code to have to install ruby, learn about ruby gems and boot the ruby vm before having to run a cli program.
- ramius345 12y agoI know this is in the vein of hacky solutions, but I tend to use jruby for the purpose of packaging up ruby applications.
- dkarapetyan 12y agoThis is a good solution. I've used this approach as well for server daemons.
- delsalk 12y agoDo you still require the JVM installed in the same way you would the Ruby's VM?
- ramius345 12y agoJRuby's ruby VM runs on top of whatever JVM you choose. You just have to make sure you have the JVM in your path.
- evmar 12y agoWithout some difference between functions and blocks, patterns like foo.each do |x| return if x == 3 end Don't "return" out of the enclosing function.
- pje 12y agoRuby's cardinal sin: there's no way to `require` code without global side effects. e.g. https://twitter.com/tomdale/status/457282269342744576 https://twitter.com/tomdale/status/457282269342744576
- roywiggins 12y agoTechnically this is true for Python as well though, right? You can't import a module without executing it.
- riffraff 12y agobut you can namespace the import, which avoids _accidental_ global pollution i.e. import foo # creates only foo require 'foo' # can change everything In both ruby and python you can execute code that changes the global namespace from within a module, but you have to go out of your way to do it in python, while in ruby it's the default. The thing is: ruby has #load(true) to load a file in anonymous module, it would just need to return that module and we could build something reasonably close to python's import. (I think there was an RCR for this many years ago)
- masklinn 12y ago> import foo # creates only foo Technically incorrect. The code within `foo` could alter anything itself accessible via an import, and it could walk up the stack to mess with the "current" stack frame of the module being defined by the import. It's generally not done and would be considered very bad form, but it's definitely possible. > In both ruby and python you can execute code that changes the global namespace from within a module, but you have to go out of your way to do it in python, while in ruby it's the default. The tweet is about monkeypatching existing structures though. And you can definitely do that in Python, including at import (though that's generally considered a Bad Idea, libraries which can do that such as gevent usually require an explicit call to patch or replace existing modules)
- deleted 12y ago
- Roboprog 12y agoMostly that was an interesting article, but there was one pain point: having to read the parameter list to a routine three (!) times. Honestly, why has no language (few languages?) come up with a scheme yet to put the formal parameters to a routine one per line, with documentation, like so: ... param-name [<type, if specified>] [<documentation>] ... Swap the name and type order for "New Jersey" style languages vs "Swiss" style languages as needed. This is actually how I used to write out my C function headers (Frack K&R layout!), even though we didn't use a doc generator tool such as Doxygen (sp?). Javadoc is only slightly less offensive, naming the parameters twice, due to slavish adherence to the altar of New Jersey formatting. I would love to see "//@ description..." after each parameter as an alternate to "* @param name description..." duplication above the parameters. "WET brain-death forever!", I guess.
- kjs3 12y agoCan you show an example of your C header style? I confess I'm not quite sure I follow you there.
- Roboprog 12y ago/* Return validated output for printing */ static t_output * val_and_xform ( int in_cnt /* input item counter, 1 based */ t_input * raw_in /* raw input, immutable, not null */ ) { ... } Further examples: https://github.com/roboprog/buzzard/blob/master/bzrt/src/bzrt_alloc.h https://github.com/roboprog/buzzard/blob/master/bzrt/src/bzr...
- kjs3 12y agoAh, got it. That has a lot going for it, documentation wise. Thanks for the illumination. I can see the style-nazis hating on it with a white-hot fury, tho.
- Veedrac 12y agoIf you want to replace floats, what with? Symbolic computation is way too expensive. Fractions are good whilst they're restricted in size but are too slow at arbitrary precision and too inaccurate with numbers not centred on 1. Fixed precision decimal floating point has numerical problems because numbers scale by too large an increment. (see footnote) Arbitrary precision floats don't solve anything unless your problem was too little precision - with the precision of a 64 bit float this is rarely the case and 128 bits is massively more than that. Arbitrary precision decimals don't solve anything either. Logarithmic number systems are a good contender but almost all operations become lossy. This is OK if you're using them as approximations - which is the common use of floats - but the fact floats are exact on the integers up to a large ceiling is useful (see Javascript) as with many other of their exactness properties. Even better might be a symmetric level-index number system; you get the advantages of logarithmic number systems but also get immunity from overflow and underflow - the only operation that isn't closed is division by 0 and since operations can't underflow all 0s are actually 0. If it were me implementing a new very-high level language with only weak cares about speed (eg. faster than doing it symbolically), I'd probably choose to use fixed precision fractions that fall back to symmetric level-index numbers instead of rounding. That way I'd get precise rationals (including int64s), immunity from underflow and overflow and a smoother error curve than floats. --- Some people are pretty surprised to hear me criticize decimal types, but they're genuinely numerically worse than binary floating point. I've talked about this in depth here: http://www.reddit.com/r/programming/comments/2ut00j/a_little_thing_to_love_about_perl_6_and_cobol/cobl3vd http://www.reddit.com/r/programming/comments/2ut00j/a_little... and I give a rough rule-of-thumb about what type to use when here: http://www.reddit.com/r/learnpython/comments/2wyeho/is_this_an_error_in_the_python_interpreter_if_not/covafmk http://www.reddit.com/r/learnpython/comments/2wyeho/is_this_...
- sshaw 12y agoReally, that's not weird at all. Far from it. For a truly different Ruby coding style checkout some of Ara T. Howard's code: https://github.com/ahoward https://github.com/ahoward.