When comparing statically typed languages to non, that's generally what "expressiveness" means to me. Any idea can be expressed through expressions in a turing complete language right? That's not a useful metric.
In Ruby a method's type-signature expresses almost nothing for example.
def f(a,b)
Does the method side-effect? What types are a or b? Are they the same type? Does the method mutate the arguments? Return a new value? What sort of values might it possibly return?In Ruby you can't express any of these things outside of naming conventions (that are entirely useless for answering any of these questions to any tooling).
Compare that to Scala:
def f(a: Int, b: Int): Int
Does the method side-effect? Almost certainly not (but it would typically depend on the containing Type). What are the types of a and b? Integers. What does it return? An Integer. Does it mutate the arguments? No; they're immutable. Is it possible for the method to return anything else? Throw an exception perhaps? That would be unexpected. Otherwise you'd write it to return a Try[Int], so you can assume more about the author's intent here.But in a statically typed language like Scala or Haskell, you can use the typechecker to express proofs about program correctness. That is a form of "expressiveness" that doesn't really exist in Ruby or Clojure (unless you use core.typed).
IOW, Ruby could use the Scala syntax I wrote, and still be just as Dynamic. You're already constrained by how you use that type within your method. You're just not expressing it concisely since you have to read the method to understand the implicit Type it's operating on.
In practice a lot of this gets naturally conflated though. Which I think you were getting at.
But if we're going to limit ourselves to functions with undeclared types, I guess I'd point to Clojure's partial as something you can't really express with Ruby. Or Pattern Matching in Elixer.
With Clojure you can actually compile your modules while developing them and discover certain classes of type errors ahead of time, so something like core.typed actually makes sense because you can run the typechecker over your code instead of having to run a separate test suite.
The main type error you always have to worry about in both languages, though, is accidentally calling functions/methods on nil at runtime. Of course that applies to null in Java too...
That doesn't mean it's just documentation though. It could tell you that something is happening you didn't expect. Like passing a Role where you expected a User. (And if you wanted to operate on both interchangeably you shoulda put an interface on it.)
> The main type error you always have to worry about in both languages, though, is accidentally calling functions/methods on nil at runtime.
Agreed. Option/Maybe types FTW. The ?-operators sometimes presented as a solution feel very half-baked in comparison (they don't return a new type so just because you remembered to use it on line 10 doesn't mean anything to lines 20, 37 and 82).
The more concise the solution the more expressive the language.
A language that supports 4*4 is more expressive than a language that only supports 4+4+4+4, languages that require more type information to be expressed are in general less expressive.
let add a b = a+b
is more expressive than func add(a:IAddition, b:IAddition) -> IAddition {
return a + b;
}
where IAddition is an interface requiring the infix + operator to be defined, and the subsequent definition of said interface, where as a more expressive language will deduce the interface from the function.Also the empty list can be expressed as a type...
class EmptyList
def empty?
true
end
endBut your definition of "expressive" is (IMO) backwards. Because the first example only actually expresses a label that only has meaning because you give it. It doesn't tell you if the method prints to STDOUT, concats a String, adds an Int, adds a Float to a Decimal (and how that would be achieved), etc.
All that, and Ruby is (IME) more verbose than Scala (and I suspect Elixir, Erlang, Clojure, etc) for any real non-trivial example.
Just take run of the mill Ruby code and convert it to Clojure. Especially anything that looks like functional programming.
And here's an attempt to objectively rank programming languages by expressiveness, measured as lines of code changed per commit: http://redmonk.com/dberkholz/2013/03/25/programming-language...