RDL: a lightweight system for adding contracts to Ruby
github.com
github.com
Here's a Babel plugin which adds design by contract to JavaScript, squeezing it into existing JS syntax (the result is really similar to Eiffel) - https://github.com/codemix/babel-plugin-contracts
and here's what it looks like in a reasonably complicated program - https://github.com/codemix/malloc/blob/master/src/index.js
(disclosure: I wrote this)
This thing can be used alongside Flow[0] and other babel plugins which use flow syntax.
Because it's yet another layer on top of other layers, because it has performance implications ... A few languages such as Vala support some form of contracts, this kind of feature is more useful when part of the language itself, not some third party babel plugin that leads to codebase fragmentation as one just had yet another layer to the compilation pipeline. No offense, that's a nice thing you wrote, I'm just not going to use this kind of thing and add more complexity for very little gain. To me tests ARE the contracts.
I would only be willing to pay the cost of transpilation with something like Typescript which adds a signification value. Babel and co ? not worthwhile.
Contracts are useful during development, they're typically stripped out in production builds, so there's no performance overhead there. If you find a bug in production, turn on your contracts and you'll quickly discover the source of the error. Contracts can validate the state of the world at runtime in a way that can never be covered by unit tests, unless your tests cover all possible inputs to your program. It doesn't mean you don't need unit tests too, they are yet another layer of safety.
I use this in combination with Flow, which means I have to transpile regardless, so the overhead is very low for me. Of course I'd like them to be part of the language proper, but for that to happen there needs to be developer demand first.
Because it's an ugly and slow run-time hack around a proper type system, but without most of the benefits of a real type-system. The compiler cannot optimize with the given types ("contracts"), it can only add run-time checks.
Check what a typed ruby can do, and how the syntax looks much more natural there.
E.g. in my typed perl (cperl) I optimize array accesses to omit out-of-bound checks when the compiler knows that the index is within the bounds. This is true for "shaped" arrays (typed arrays with a constant range), if the constant upper bound of a loop is within the shape, or if the loop just iterates over an array.
If the compiler knows that the given number to an unary op is only an integer, the optimized integer method can be used instead. If both arguments to a binary numeric op are integers, the optimized integer method can be used. ...
I understand that many people say that with static typing if it compiles it works, but I remember using debuggers quite a lot.
I'd say that if one wants types there are plenty of languages to choose from. There is Crystal if one likes Ruby.
Contracts are interesting. I remember Eiffel and other languages. Those could be useful regardless of the kind of typing the language use.
BTW, invariants are another useful tool when designing some algorithms and were neglected recently.
Java is a pretty terrible language when it comes to static types. If you have a language that allows you to work with types in a succinct and expressive way, you end up with a powerful modelling tool that can help you reason about and design programs. The side-effect of this is to have far less runtime crashes (you know what cases to check for, like nullable data), great documentation for your future self and your fellow devs, and a greater liberty to do large scale refactors without worrying about breaking things.
Kotlin is an excellent example of such a language, and I'd highly encourage anyone, especially devs with a strong Java background, to try it out.
In contrast, contributing to an existing codebase in PHP or Rails is much harder. Nothing will stop you from sending the wrong arguments to a method, or adding members to classes that should not be modified, or even simply refactoring and forgetting to change all of the usages of the types and methods you modified.
Which really means there's a big difference between static types that everyone knows and uses, and those that someone invented for particular use. That difference seems more significant than the static/dynamic divide.
Many people's impressions of static languages are based on Java from 10 years ago. It's really quite different now.
I'm with you on the excess typing thing, but I still think types are useful just for tool integration. One day I hope that a popular language will emerge that presents an AST so that we can have non-type based refactoring browsers like Smalltalk's.
Most of my time is spent playing the game "which type is this?".
Yeah, one can write unit tests to play the role of the compiler's type checker, but in the enterprise unit tests are pretty low in the deliverables list, usually the bullet following documentation.
Unfortunately I feel like this is where types are least likely to be useful in Ruby and Python. Static typing means that you know the type of a value coming into your function, but you can achieve exactly the same thing by dynamically profiling the type, just-in-time specialising the function for it, and then on each call all you need to do is check that the type is what was expected. The check will probably be just one machine word comparison. So static typing saves you just one instruction!
However it would remove the time, memory and complexity needed for the profiling. I'm not sure that's really a big overhead though.
I work on implementing existing programming languages using new technology, and in some cases we basically discard static typing information for the purposes of optimisation, because we can work out better information for ourselves at runtime using profiling.
Ruby also has another popular and mature library that I am surprised nobody has mentioned yet named 'Contracts.ruby'
https://github.com/egonSchiele/contracts.ruby
This syntax is also more appealing to my eyes than what rdl provides.
Contract Num => Num
def double(x)
x * 2
endhttps://github.com/smizell/snug
For me, it is useful for runtime checking data that may come across the web.
(disclaimer: I just graduated from PLUM)
I'm an undergraduate, so my experience with PLUM is just that of an admirer looking in ;)
Piggybacking on this: apart from F*, does someone know of a language or support for existing ones for contracts, loop-invariants and possibly verification?
Dafny and Spec# aren't very general-purpose and apart from those nothing much comes to my mind.
Untested code follows:
procedure swap(a: in out integer, b: in out integer)
with
pre => a <> b -- just for example purposes
post => (a == b'old) and (b == a'old)
is declare
t: integer;
begin
t := a;
a := b;
b := t;
end
(Sorry, couldn't think of a sensible small example which uses both pre and post, hence the terrible precondition.)Note that the postcondition can refer to the old values of the variables with the 'old suffix --- the values will be automatically saved on entry to the function and used for comparison later.
Unfortunately the pre- and postconditions have to be specified in the public part of the module, so can't see any private module variables, which forces you to jump through hoops if you don't want to expose your module's internal state (e.g. checking to make sure that functions on a state machine are called in the right order).
You also get type invariants, where you can specify an expression which must always be true for a number:
subtype Even is integer
with dynamic_predicate => (Even mod 2) == 0;
Or, if you really want your mind blown: subtype Even is integer
with dynamic_predicate => (for some N in integer => (Even == N*2));
There's also a static_predicate form which only supports a restricted predicate expression but which can be checked at compile time.Also: SPARK is another one I forgot. I guess I'll have to think of a nice example and just implement it in a Ada, F* and with RDL to get a hang of the differences and features.
Cant upvote you enough. I am getting the sense that DHH dictates more of Ruby rather then Matz. ( Which isn't necessarily a good or bad thing )