rdl 1.1.0 → 1.1.1.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGES.md +25 -0
- data/README.md +104 -64
- data/extras/type_tests/%.rb +171 -0
- data/extras/type_tests/&.rb +159 -0
- data/extras/type_tests/**.rb +222 -0
- data/extras/type_tests/*.rb +177 -0
- data/extras/type_tests/+.rb +170 -0
- data/extras/type_tests/-.rb +171 -0
- data/extras/type_tests/1scomp.rb +157 -0
- data/extras/type_tests/<.rb +170 -0
- data/extras/type_tests/<<.rb +159 -0
- data/extras/type_tests/>>.rb +159 -0
- data/extras/type_tests/[].rb +163 -0
- data/extras/type_tests/^.rb +159 -0
- data/extras/type_tests/abs.rb +155 -0
- data/extras/type_tests/abs2.rb +164 -0
- data/extras/type_tests/angle.rb +157 -0
- data/extras/type_tests/arg.rb +157 -0
- data/extras/type_tests/bit_length.rb +157 -0
- data/extras/type_tests/ceil.rb +157 -0
- data/extras/type_tests/ceilRational.rb +160 -0
- data/extras/type_tests/conj.rb +158 -0
- data/extras/type_tests/defwhere.rb +86 -0
- data/extras/type_tests/denominator.rb +157 -0
- data/extras/type_tests/div.rb +172 -0
- data/extras/type_tests/divslash.rb +179 -0
- data/extras/type_tests/even?.rb +157 -0
- data/extras/type_tests/fdiv.rb +244 -0
- data/extras/type_tests/finite?.rb +157 -0
- data/extras/type_tests/floor.rb +157 -0
- data/extras/type_tests/floorRational.rb +161 -0
- data/extras/type_tests/hash.rb +157 -0
- data/extras/type_tests/imag.rb +158 -0
- data/extras/type_tests/infinite?.rb +157 -0
- data/extras/type_tests/modulo.rb +171 -0
- data/extras/type_tests/nan?.rb +157 -0
- data/extras/type_tests/neg.rb +155 -0
- data/extras/type_tests/next.rb +157 -0
- data/extras/type_tests/next_float.rb +157 -0
- data/extras/type_tests/numerator.rb +157 -0
- data/extras/type_tests/phase.rb +157 -0
- data/extras/type_tests/prev_float.rb +157 -0
- data/extras/type_tests/quo.rb +179 -0
- data/extras/type_tests/rationalize.rb +157 -0
- data/extras/type_tests/rationalizeArg.rb +198 -0
- data/extras/type_tests/real.rb +157 -0
- data/extras/type_tests/real?.rb +157 -0
- data/extras/type_tests/round.rb +157 -0
- data/extras/type_tests/roundArg.rb +169 -0
- data/extras/type_tests/size.rb +157 -0
- data/extras/type_tests/to_c.rb +157 -0
- data/extras/type_tests/to_f.rb +155 -0
- data/extras/type_tests/to_i.rb +157 -0
- data/extras/type_tests/to_r.rb +157 -0
- data/extras/type_tests/to_s.rb +157 -0
- data/extras/type_tests/truncate.rb +157 -0
- data/extras/type_tests/truncateArg.rb +166 -0
- data/extras/type_tests/type tests +1 -0
- data/extras/type_tests/zero?.rb +155 -0
- data/extras/type_tests/|.rb +159 -0
- data/lib/rdl/contracts/and.rb +1 -1
- data/lib/rdl/contracts/flat.rb +2 -2
- data/lib/rdl/contracts/proc.rb +2 -1
- data/lib/rdl/types/.#lexer.rex +1 -0
- data/lib/rdl/types/dependent_arg.rb +47 -0
- data/lib/rdl/types/finitehash.rb +5 -5
- data/lib/rdl/types/generic.rb +3 -3
- data/lib/rdl/types/lexer.rex +5 -2
- data/lib/rdl/types/lexer.rex.rb +3 -0
- data/lib/rdl/types/method.rb +144 -15
- data/lib/rdl/types/nominal.rb +1 -1
- data/lib/rdl/types/parser.racc +6 -1
- data/lib/rdl/types/parser.tab.rb +272 -245
- data/lib/rdl/types/tuple.rb +1 -1
- data/lib/rdl/types/type_inferencer.rb +7 -7
- data/lib/rdl/wrap.rb +16 -11
- data/rdl.gemspec +3 -3
- data/test/test_dsl.rb +4 -5
- data/test/test_le.rb +5 -5
- data/test/test_lib_types.rb +34 -34
- data/test/test_member.rb +3 -3
- data/test/test_type_contract.rb +63 -1
- data/test/test_types.rb +2 -0
- data/types/ruby-2.x/_aliases.rb +2 -2
- data/types/ruby-2.x/bigdecimal.rb +246 -12
- data/types/ruby-2.x/bignum.rb +253 -0
- data/types/ruby-2.x/complex.rb +111 -22
- data/types/ruby-2.x/fixnum.rb +238 -31
- data/types/ruby-2.x/float.rb +217 -35
- data/types/ruby-2.x/integer.rb +17 -16
- data/types/ruby-2.x/numeric.rb +31 -21
- data/types/ruby-2.x/rational.rb +196 -18
- metadata +67 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 89545c8109d2fcc279258745760772a7114d6842
|
4
|
+
data.tar.gz: 308333136875d3904304c6475d2503e61adb0a8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b967fbe0e88dc13e802cb71c85e603d4886cb4beb98b708069507f6bafc7a4507909c8b2e003cdc73f28d926a7de45c610e3a9d3b8a0ba7708c738f25850c56f
|
7
|
+
data.tar.gz: 6f6506ddffe97004b10d01cec5fa1ed3897aa429a2d40c2cbc708440553ad6eab53b746a2084bee9d8861dc67b6b80bfcb6851c25825ddde674a7c2a104b4abe
|
data/.travis.yml
CHANGED
data/CHANGES.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Change log!
|
2
|
+
|
3
|
+
## [Unreleased]
|
4
|
+
|
5
|
+
## [1.1.1] - 2016-05-21
|
6
|
+
### Fixed
|
7
|
+
- Update code to eliminate Ruby 2.3 warning messages
|
8
|
+
- Fixed errors in post conditions of numeric types (incorrect number of args)
|
9
|
+
- Added syntax highlighting in README.md as pointed out by jsyeo
|
10
|
+
- Comprehensive changes to types of Numeric subclass methods to make types more specific & accurate
|
11
|
+
- Changed superclasses of numeric classes to be `Numeric`
|
12
|
+
|
13
|
+
### Added
|
14
|
+
- Higher-order types and tests for them
|
15
|
+
- Dependent types
|
16
|
+
- `/extras` directory, which contains random type tests for numeric subclass method types
|
17
|
+
- `BigDecimal` added to alias `%real`
|
18
|
+
- Changelog added!
|
19
|
+
|
20
|
+
## [1.1.0] - 2016-01-03
|
21
|
+
### Added
|
22
|
+
- Added much enhanced `rdl_query` facility and accompanying command-line script.
|
23
|
+
|
24
|
+
## [1.0.0] - 2015-12-18
|
25
|
+
- First release!
|
data/README.md
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
[![Gem Version](https://badge.fury.io/rb/rdl.svg)](https://badge.fury.io/rb/rdl)
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/rdl.svg)](https://badge.fury.io/rb/rdl) [![Build Status](https://travis-ci.org/plum-umd/rdl.svg?branch=master)](https://travis-ci.org/plum-umd/rdl)
|
2
|
+
|
2
3
|
|
3
4
|
# Introduction
|
4
5
|
|
5
6
|
RDL is a lightweight system for adding contracts to Ruby. A *contract* decorates a method with assertions describing what the method assumes about its inputs (called a *precondition*) and what the method guarantees about its outputs (called a *postcondition*). For example, using RDL we can write
|
6
7
|
|
7
|
-
```
|
8
|
+
```ruby
|
8
9
|
require 'rdl'
|
9
10
|
|
10
11
|
pre { |x| x > 0 }
|
@@ -21,7 +22,7 @@ RDL contracts are enforced at method entry and exit. For example, if we call `sq
|
|
21
22
|
|
22
23
|
In addition to arbitrary pre- and post-conditions, RDL also has extensive support for contracts that are *types*. For example, we can write the following in RDL:
|
23
24
|
|
24
|
-
```
|
25
|
+
```ruby
|
25
26
|
require 'rdl'
|
26
27
|
|
27
28
|
type '(Fixnum, Fixnum) -> String'
|
@@ -32,7 +33,7 @@ This indicates that `m` is that method that returns a `String` if given two `Fix
|
|
32
33
|
|
33
34
|
RDL contracts and types are stored in memory at run time, so it's also possible for programs to query them. RDL includes lots of contracts and types for the core and standard libraries. Since those methods are generally trustworthy, RDL doesn't actually enforce the contracts (since that would add overhead), but they are available to search and query. RDL includes a small script `rdl_query` to look up type information from the command line. Note you might need to put the argument in quotes depending on your shell.
|
34
35
|
|
35
|
-
```
|
36
|
+
```shell
|
36
37
|
$ rdl_query String#include? # print type for instance method of another class
|
37
38
|
$ rdl_query Pathname.glob # print type for singleton method of a class
|
38
39
|
$ rdl_query Array # print types for all methods of a class
|
@@ -44,7 +45,7 @@ $ rdl_query "(..., Fixnum, ...) -> ." # print all methods that take a Fixnum as
|
|
44
45
|
|
45
46
|
See below for more details of the query format. The `rdl_query` method performs the same function as long as the gem is loaded, so you can use this in `irb`.
|
46
47
|
|
47
|
-
```
|
48
|
+
```ruby
|
48
49
|
$ irb
|
49
50
|
> require 'rdl'
|
50
51
|
=> true
|
@@ -76,7 +77,7 @@ Use `require 'rdl'` to load the RDL library. If you want to use the core and sta
|
|
76
77
|
|
77
78
|
If you're using Ruby on Rails, you can similarly `require 'rails_types'` to load in type annotations for the current `Rails::VERSION::STRING`. More specifically, add the following lines in `application.rb` after the `Bundler.require` call. (This placement is needed so the Rails version string is available and the Rails environment is loaded):
|
78
79
|
|
79
|
-
```
|
80
|
+
```ruby
|
80
81
|
require 'rdl'
|
81
82
|
require 'rdl_types'
|
82
83
|
require 'rails_types'
|
@@ -118,7 +119,7 @@ A type string generally has the form `(typ1, ..., typn) -> typ` indicating a met
|
|
118
119
|
|
119
120
|
A nominal type is simply a class name, and it matches any object of that class or any subclass.
|
120
121
|
|
121
|
-
```
|
122
|
+
```ruby
|
122
123
|
type String, :insert, '(Fixnum, String) -> String'
|
123
124
|
```
|
124
125
|
|
@@ -126,12 +127,12 @@ type String, :insert, '(Fixnum, String) -> String'
|
|
126
127
|
|
127
128
|
The nominal type `NilClass` can also be written as `nil`. The only object of this type is `nil`:
|
128
129
|
|
129
|
-
```
|
130
|
+
```ruby
|
130
131
|
type IO, :close, '() -> nil' # IO#close always returns nil
|
131
132
|
```
|
132
133
|
|
133
134
|
Currently, `nil` is treated as if it were an instance of any class.
|
134
|
-
```
|
135
|
+
```ruby
|
135
136
|
x = "foo"
|
136
137
|
x.insert(0, nil) # RDL does not report a type error
|
137
138
|
```
|
@@ -140,7 +141,7 @@ We chose this design based on prior experience with static type systems for Ruby
|
|
140
141
|
### Top Type (%any)
|
141
142
|
|
142
143
|
RDL includes a special "top" type `%any` that matches any object:
|
143
|
-
```
|
144
|
+
```ruby
|
144
145
|
type Object, :=~, '(%any) -> nil'
|
145
146
|
```
|
146
147
|
We call this the "top" type because it is the top of the subclassing hierarchy RDL uses. Note that `%any` is more general than `Object`, because not all classes inherit from `Object`, e.g., `BasicObject` does not.
|
@@ -149,7 +150,7 @@ We call this the "top" type because it is the top of the subclassing hierarchy R
|
|
149
150
|
|
150
151
|
Many Ruby methods can take several different types of arguments or return different types of results. The union operator `or` can be used to indicate a position where multiple types are possible.
|
151
152
|
|
152
|
-
```
|
153
|
+
```ruby
|
153
154
|
type IO, :putc, '(Numeric or String) -> %any'
|
154
155
|
type String, :getbyte, '(Fixnum) -> Fixnum or nil'
|
155
156
|
```
|
@@ -160,7 +161,7 @@ Note that for `getbyte`, we could leave off the `nil`, but we include it to matc
|
|
160
161
|
|
161
162
|
Sometimes Ruby methods have several different type signatures. (In Java these would be called *overloaded* methods.) In RDL, such methods are assigned a set of type signatures:
|
162
163
|
|
163
|
-
```
|
164
|
+
```ruby
|
164
165
|
type String, :[], '(Fixnum) -> String or nil'
|
165
166
|
type String, :[], '(Fixnum, Fixnum) -> String or nil'
|
166
167
|
type String, :[], '(Range or Regexp) -> String or nil'
|
@@ -173,7 +174,7 @@ We say the method's type is the *intersection* of the types above.
|
|
173
174
|
|
174
175
|
When this method is called at run time, RDL checks that at least one type signature matches the call:
|
175
176
|
|
176
|
-
```
|
177
|
+
```ruby
|
177
178
|
"foo"[0] # matches first type
|
178
179
|
"foo"[0,2] # matches second type
|
179
180
|
"foo"[0..2] # matches third type
|
@@ -183,7 +184,7 @@ When this method is called at run time, RDL checks that at least one type signat
|
|
183
184
|
|
184
185
|
Notice that union types in arguments could also be written as intersection types of methods, e.g., instead of the third type of `[]` above we could have equivalently written
|
185
186
|
|
186
|
-
```
|
187
|
+
```ruby
|
187
188
|
type String, :[], '(Range) -> String or nil'
|
188
189
|
type String, :[], '(Regexp) -> String or nil'
|
189
190
|
```
|
@@ -192,13 +193,13 @@ type String, :[], '(Regexp) -> String or nil'
|
|
192
193
|
|
193
194
|
Optional arguments are denoted in RDL by putting `?` in front of the argument's type. For example:
|
194
195
|
|
195
|
-
```
|
196
|
+
```ruby
|
196
197
|
type String, :chomp, '(?String) -> String'
|
197
198
|
```
|
198
199
|
|
199
200
|
This is actually just a shorthand for an equivalent intersection type:
|
200
201
|
|
201
|
-
```
|
202
|
+
```ruby
|
202
203
|
type String, :chomp, '() -> String'
|
203
204
|
type String, :chomp, '(String) -> String'
|
204
205
|
```
|
@@ -211,7 +212,7 @@ Like Ruby, RDL allows optional arguments to appear anywhere in a method's type s
|
|
211
212
|
|
212
213
|
In RDL, `*` is used to decorate an argument that may appear zero or more times. Currently in RDL this annotation may only appear on the rightmost argument. For example, `String#delete` takes one or more `String` arguments:
|
213
214
|
|
214
|
-
```
|
215
|
+
```ruby
|
215
216
|
type String, :delete, '(String, *String) -> String'
|
216
217
|
```
|
217
218
|
|
@@ -219,31 +220,73 @@ type String, :delete, '(String, *String) -> String'
|
|
219
220
|
|
220
221
|
RDL allows arguments to be named, for documentation purposes. Names are given after the argument's type, and they do not affect type contract checking in any way. For example:
|
221
222
|
|
222
|
-
```
|
223
|
+
```ruby
|
223
224
|
type Fixnum, :to_s, '(?Fixnum base) -> String'
|
224
225
|
```
|
225
226
|
|
226
227
|
Here we've named the first argument of `to_s` as `base` to give some extra hint as to its meaning.
|
227
228
|
|
228
|
-
###
|
229
|
+
### Dependent Types
|
229
230
|
|
230
|
-
|
231
|
+
RDL allows for refinement predicates to be attached to named arguments. These predicates are then checked when the method is called and returns. For instance:
|
231
232
|
|
233
|
+
```ruby
|
234
|
+
type '(Float x {{ x>=0 }}) -> Float y {{ y>=0 }}'
|
235
|
+
def sqrt(x)
|
236
|
+
# return the square root of x
|
237
|
+
end
|
232
238
|
```
|
233
|
-
|
239
|
+
|
240
|
+
Here, RDL will check that the `sqrt` method is called on an argument of type `Float` which is greater than or equal to 0, and it will check the same of the return value of the method. Note that, in effect, dependent type contracts can be used in place of pre and post contracts.
|
241
|
+
|
242
|
+
Dependencies can also exist across a method's arguments and return value:
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
type '(Fixnum x {{ x>y }}, Fixnum y) -> Float z {{ z==(x+y) }}'
|
246
|
+
def m(x,y) ... end
|
234
247
|
```
|
235
248
|
|
236
|
-
|
249
|
+
Any arbitrary code can be placed between the double braces of a type refinement, and RDL will dynmically check that this predicate evaluates to true, or raise a type error if it evaluates to false.
|
250
|
+
|
251
|
+
### Higher-Order Contracts
|
252
|
+
|
253
|
+
RDL supports contracts for arguments or return values which are themselves `Proc` objects. Simply enclose the corresponding argument's type with braces to denote that it is a `Proc`. For example:
|
237
254
|
|
238
|
-
|
255
|
+
```ruby
|
256
|
+
type '(Fixnum, {(Fixnum) -> Fixnum}) -> Fixnum'
|
257
|
+
def m(x, y) ... end
|
258
|
+
```
|
259
|
+
|
260
|
+
The type annotation above states that the method m takes two arguments: one of type `Fixnum`, and another which is a `Proc` which itself takes a `Fixnum` and returns a `Fixnum`. A `Proc` may be the return value of a method as well:
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
type '(Fixnum) -> {(Float) -> Float}'
|
264
|
+
def m(x) ... end
|
265
|
+
```
|
266
|
+
|
267
|
+
These higher-order contracts are checked by wrapping the corresponding `Proc` argument/return in a new `Proc` which checks that the type contract holds.
|
268
|
+
|
269
|
+
A type contract can be provided for a method block as well. The block's type should be included after the method argument types:
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
type '(Fixnum, Float) {(Fixnum, String) -> String } -> Float'
|
273
|
+
def m(x,y,&blk) ... end
|
274
|
+
```
|
239
275
|
|
240
|
-
|
276
|
+
Note that this notation will work whether or not a method block is explicitly referenced in the parameters, i.e., whether or not `&blk` is included above. Finally, dependent types work across higher order contracts:
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
type '(Fixnum x, Float y) -> {(Fixnum z {{ z>y }}) -> Fixnum}'
|
280
|
+
def m(x,y,&blk) ... end
|
281
|
+
```
|
282
|
+
|
283
|
+
The type contract above states that method `m` returns a `Proc` which takes a `Fixnum z` which must be greater than the argument `Float y`. Whenver this `Proc` is called, it will be checked that this contract holds.
|
241
284
|
|
242
285
|
### Class/Singleton Method Types
|
243
286
|
|
244
287
|
RDL method signatures can be used both for instance methods and for class methods (often called *singleton methods* in Ruby). To indicate a type signature applies to a singleton method, prefix the method name with `self.`:
|
245
288
|
|
246
|
-
```
|
289
|
+
```ruby
|
247
290
|
type File, 'self.dirname', '(String file) -> String dir'
|
248
291
|
```
|
249
292
|
|
@@ -251,7 +294,7 @@ type File, 'self.dirname', '(String file) -> String dir'
|
|
251
294
|
|
252
295
|
Type signatures can be added to `initialize` by giving a type signature for `self.new`:
|
253
296
|
|
254
|
-
```
|
297
|
+
```ruby
|
255
298
|
type File, 'self.new', '(String file, ?String mode, ?String perm, ?Fixnum opt) -> File'
|
256
299
|
```
|
257
300
|
|
@@ -259,7 +302,7 @@ type File, 'self.new', '(String file, ?String mode, ?String perm, ?Fixnum opt) -
|
|
259
302
|
|
260
303
|
Some Ruby methods are intended to take any object that has certain methods. RDL uses *structural types* to denote such cases:
|
261
304
|
|
262
|
-
```
|
305
|
+
```ruby
|
263
306
|
type IO, :puts, '(*[to_s: () -> String]) -> nil'
|
264
307
|
```
|
265
308
|
|
@@ -271,23 +314,24 @@ The actual checking that RDL does here varies depending on what type information
|
|
271
314
|
|
272
315
|
Not to be confused with types for singleton methods, RDL includes *singleton types* that denote positions that always have one particular value; this typically happens only in return positions. For example, `Dir#mkdir` always returns the value 0:
|
273
316
|
|
274
|
-
```
|
317
|
+
```ruby
|
275
318
|
type Dir, 'self.mkdir', '(String, ?Fixnum) -> 0'
|
276
319
|
```
|
277
320
|
|
278
321
|
In RDL, any integer or floating point number denotes a singleton type. Arbitrary values can be turned into singleton types by wrapping them in `${.}`. For example, `Float#angle` always returns 0 or pi.
|
279
322
|
|
280
|
-
```
|
323
|
+
```ruby
|
281
324
|
type Float, :angle, '() -> 0 or ${Math::PI}'
|
282
325
|
```
|
283
326
|
|
284
327
|
RDL checks if a value matches a singleton type using `equal?`. As a consequence, singleton string types aren't currently possible.
|
285
328
|
|
329
|
+
|
286
330
|
### Self Type
|
287
331
|
|
288
332
|
Consider a method that returns `self`:
|
289
333
|
|
290
|
-
```
|
334
|
+
```ruby
|
291
335
|
class A
|
292
336
|
def id
|
293
337
|
self
|
@@ -297,7 +341,7 @@ end
|
|
297
341
|
|
298
342
|
If that method might be inherited, we can't just give it a nominal type, because it will return a different object type in a subclass:
|
299
343
|
|
300
|
-
```
|
344
|
+
```ruby
|
301
345
|
class B < A
|
302
346
|
end
|
303
347
|
|
@@ -308,7 +352,7 @@ B.new.id # type error, returns a B
|
|
308
352
|
|
309
353
|
To solve this problem, RDL includes a special type `self` for this situation:
|
310
354
|
|
311
|
-
```
|
355
|
+
```ruby
|
312
356
|
type A, :id, '() -> self'
|
313
357
|
A.new.id # okay, returns self
|
314
358
|
B.new.id # also okay, returns self
|
@@ -320,7 +364,7 @@ Note that type `self` means *exactly* the self object, i.e., it is a singleton t
|
|
320
364
|
|
321
365
|
RDL allows types to be aliases to make them faster to write down and more readable. All type aliases begin with `%`. RDL has one built-in alias, `%bool`, which is shorthand for `TrueClass or FalseClass`:
|
322
366
|
|
323
|
-
```
|
367
|
+
```ruby
|
324
368
|
type String, :==, '(%any) -> %bool'
|
325
369
|
```
|
326
370
|
|
@@ -328,7 +372,7 @@ Note it is not a bug that `==` is typed to allow any object. Though you would th
|
|
328
372
|
|
329
373
|
Method `type_alias(name, typ)` can be used to create a user-defined type alias, where `name` must begin with `%`:
|
330
374
|
|
331
|
-
```
|
375
|
+
```ruby
|
332
376
|
type_alias '%real', 'Integer or Float or Rational'
|
333
377
|
type_alias '%string', '[to_str: () -> String]'
|
334
378
|
type_alias '%path', '%string or Pathname'
|
@@ -340,7 +384,7 @@ Type aliases have to be created before they are used (so above, `%path` must be
|
|
340
384
|
|
341
385
|
RDL supports *parametric polymorphism* for classes, a.k.a. *generics*. The `type_params` method names the type parameters of the class, and those parameters can then be used inside type signatures:
|
342
386
|
|
343
|
-
```
|
387
|
+
```ruby
|
344
388
|
class Array
|
345
389
|
type_params [:t], :all?
|
346
390
|
|
@@ -352,7 +396,7 @@ Here the first argument to `type_params` is a list of symbols or strings that na
|
|
352
396
|
|
353
397
|
Generic types are applied to type arguments using `<...>` notation, e.g., `Array<Fixnum>` is an `Array` class where `t` is replaced by `Fixnum`. Thus, for example, if `o` is an `Array<Fixnum>`, then `o.shift` returns `Fixnum`. As another example, here is the type for the `[]` method of `Array`:
|
354
398
|
|
355
|
-
```
|
399
|
+
```ruby
|
356
400
|
type Array, :[], '(Range) -> Array<t>'
|
357
401
|
type Array, :[], '(Fixnum or Float) -> t'
|
358
402
|
type Array, :[], '(Fixnum, Fixnum) -> Array<t>'
|
@@ -366,14 +410,14 @@ Thus, by default, even though `Array` is declared to take type parameters, by de
|
|
366
410
|
|
367
411
|
To fully enforce generic types, RDL requires that the developer `instantiate!` an object with the desired type parameters:
|
368
412
|
|
369
|
-
```
|
413
|
+
```ruby
|
370
414
|
x = [1,2]
|
371
415
|
x.instantate!('Fixnum')
|
372
416
|
x.push("three") # type error
|
373
417
|
```
|
374
418
|
|
375
419
|
Note that the instantiated type is associated with the object, not the variable:
|
376
|
-
```
|
420
|
+
```ruby
|
377
421
|
y = x
|
378
422
|
y.push("three") # also a type error
|
379
423
|
```
|
@@ -381,7 +425,7 @@ y.push("three") # also a type error
|
|
381
425
|
When RDL instantiates an object with type parameters, it needs to ensure the object's contents are consistent with the type. Currently this is enforced using the second parameter to `type_params`, which must name a method that behaves like `Array#all?`, i.e., it iterates through the contents, checking that a block argument is satisfied. As seen above, for `Array` we call `type_params(:t, :all?)`. Then at the call `x.instantiate('Fixnum')`, RDL will call `Array#all?` to iterate through the contents of `x` to check they have type `Fixnum`.
|
382
426
|
|
383
427
|
RDL also includes a `deinstantiate!` method to remove the type instantiation from an object:
|
384
|
-
```
|
428
|
+
```ruby
|
385
429
|
x.deinstantiate!
|
386
430
|
x.push("three") # no longer a type error
|
387
431
|
```
|
@@ -401,14 +445,14 @@ A type such as `Array<Fixnum>` is useful for homogeneous arrays, where all eleme
|
|
401
445
|
|
402
446
|
RDL includes special *tuple types* to handle this situation. Tuple types are written `[t1, ..., tn]`, denoting an `Array` of `n` elements of types `t1` through `tn`, in that order. For example, `[1, "two"]` has type `[Fixnum, String]`. As another example, here is the type of `Process#getrlimit`, which returns a two-element array of `Fixnums`:
|
403
447
|
|
404
|
-
```
|
448
|
+
```ruby
|
405
449
|
type Process, 'self.getrlimit', '(Symbol or String or Fixnum resource) -> [Fixnum, Fixnum] cur_max_limit'
|
406
450
|
```
|
407
451
|
|
408
452
|
### Finite Hash Types
|
409
453
|
|
410
454
|
Similarly to tuple types, RDL also supports *finite hash types* for heterogenous hashes. Finite hash types are written `{k1 => v1, ..., kn => vn}` to indicate a `Hash` with `n` mappings of type `ki` maps to `vi`. The `ki` may be strings, integers, floats, or constants denoted with `${.}`. If a key is a symbol, then the mapping should be written `ki: vi`. In the latter case, the `{}`'s can be left off:
|
411
|
-
```
|
455
|
+
```ruby
|
412
456
|
type MyClass, :foo, '(a: Fixnum, b: String) { () -> %any } -> %any'
|
413
457
|
```
|
414
458
|
Here `foo`, takes a hash where key `:a` is mapped to a `Fixnum` and key `:b` is mapped to a `String`. Similarly, `{'a'=>Fixnum, 2=>String}` types a hash where keys `'a'` and `2` are mapped to a `Fixnum` and `String`, respectively. Both syntaxes can be used to define hash types.
|
@@ -431,21 +475,21 @@ As discussed above, RDL includes a small script, `rdl_query`, to look up type in
|
|
431
475
|
|
432
476
|
* Instance methods can be looked up as `Class#method`.
|
433
477
|
|
434
|
-
```
|
478
|
+
```shell
|
435
479
|
$ rdl_query String#include?
|
436
480
|
String#include?: (String) -> TrueClass or FalseClass
|
437
481
|
```
|
438
482
|
|
439
483
|
* Singleton (class) methods can be looked up as `Class.method`.
|
440
484
|
|
441
|
-
```
|
485
|
+
```shell
|
442
486
|
$ rdl_query Pathname.glob
|
443
487
|
Pathname.glob: (String p1, ?String p2) -> Array<Pathname>
|
444
488
|
```
|
445
489
|
|
446
490
|
* All methods of a class can be listed by passing the class name `Class`.
|
447
491
|
|
448
|
-
```
|
492
|
+
```shell
|
449
493
|
$ rdl_query Array
|
450
494
|
&: (Array<u>) -> Array<t>
|
451
495
|
*: (String) -> String
|
@@ -454,7 +498,7 @@ $ rdl_query Array
|
|
454
498
|
|
455
499
|
* Methods can also be search for by their type signature:
|
456
500
|
|
457
|
-
```
|
501
|
+
```shell
|
458
502
|
$ rdl_query "(Fixnum) -> Fixnum" # print all methods of type (Fixnum) -> Fixnum
|
459
503
|
BigDecimal.limit: (Fixnum) -> Fixnum
|
460
504
|
Dir#pos=: (Fixnum) -> Fixnum
|
@@ -463,7 +507,7 @@ Dir#pos=: (Fixnum) -> Fixnum
|
|
463
507
|
|
464
508
|
The type signature uses the standard RDL syntax, with two extensions: `.` can be used as a wildcard to match any type, and `...` can be used to match any sequence of arguments.
|
465
509
|
|
466
|
-
```
|
510
|
+
```shell
|
467
511
|
$ rdl_query "(.) -> ." # methods that take one argument and return anything
|
468
512
|
$ rdl_query "(Fixnum, .) -> ." # methods that take two arguments, the first of which is a Fixnum
|
469
513
|
$ rdl_query "(Fixnum, ...) -> ." # methods whose first argument is a Fixnum
|
@@ -479,6 +523,10 @@ Note that aside from `.` and `...`, the matching is exact. For example `(Fixnum)
|
|
479
523
|
|
480
524
|
Here are some research papers we have written exploring contracts, types, and Ruby.
|
481
525
|
|
526
|
+
* Brianna M. Ren and Jeffrey S. Foster.
|
527
|
+
[Just-in-Time Static Type Checking for Dynamic Languages](http://www.cs.umd.edu/~jfoster/papers/pldi16.pdf).
|
528
|
+
In ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI), Santa Barbara, CA, June 2016.
|
529
|
+
|
482
530
|
* T. Stephen Strickland, Brianna Ren, and Jeffrey S. Foster.
|
483
531
|
[Contracts for Domain-Specific Languages in Ruby](http://www.cs.umd.edu/~jfoster/papers/dls12.pdf).
|
484
532
|
In Dynamic Languages Symposium (DLS), Portland, OR, October 2014.
|
@@ -513,7 +561,7 @@ In Object-Oriented Program Languages and Systems (OOPS) Track at ACM Symposium o
|
|
513
561
|
|
514
562
|
# Copyright
|
515
563
|
|
516
|
-
Copyright (c) 2014-
|
564
|
+
Copyright (c) 2014-2016, University of Maryland, College Park. All rights reserved.
|
517
565
|
|
518
566
|
# Contributors
|
519
567
|
|
@@ -523,17 +571,10 @@ Copyright (c) 2014-2015, University of Maryland, College Park. All rights reserv
|
|
523
571
|
* [Brianna M. Ren](https://www.cs.umd.edu/~bren/)
|
524
572
|
* [T. Stephen Strickland](https://www.cs.umd.edu/~sstrickl/)
|
525
573
|
* Alexander T. Yu
|
526
|
-
|
527
|
-
# RDL Build Status
|
528
|
-
|
529
|
-
[![Build Status](https://travis-ci.org/plum-umd/rdl.svg?branch=master)](https://travis-ci.org/plum-umd/rdl)
|
574
|
+
* Milod Kazerounian
|
530
575
|
|
531
576
|
# TODO list
|
532
577
|
|
533
|
-
* ProcContract, Wrap, MethodType, support higher-order contracts for blocks
|
534
|
-
* And higher-order type checking
|
535
|
-
* Block passed to contracts don't work yet
|
536
|
-
|
537
578
|
* How to check whether initialize? is user-defined? method_defined? always
|
538
579
|
returns true, meaning wrapping isn't fully working with initialize.
|
539
580
|
|
@@ -548,27 +589,26 @@ Copyright (c) 2014-2015, University of Maryland, College Park. All rights reserv
|
|
548
589
|
|
549
590
|
* Rails types
|
550
591
|
|
551
|
-
* Proc types
|
552
|
-
|
553
592
|
* Deferred contracts on new (watch for class addition)
|
554
593
|
|
555
594
|
* DSL contracts
|
556
595
|
|
557
|
-
*
|
596
|
+
* double-splat arguments, which bind to an arbitrary set of keywords
|
558
597
|
|
559
|
-
*
|
598
|
+
* included versus extended modules, e.g., Kernel is included in Object, so its
|
599
|
+
class methods become Object's instance methods
|
560
600
|
|
561
|
-
*
|
562
|
-
+ Object, so its class methods become Object's instance methods.
|
563
|
-
|
564
|
-
* Better story for different types for different Ruby versions.
|
601
|
+
* Better story for different types for different Ruby versions
|
565
602
|
|
566
603
|
* Better query facility (more kinds of searches). Contract queries?
|
567
604
|
|
568
605
|
* Write documentation on: Raw Contracts and Types, RDL Configuration, Code Overview
|
569
606
|
|
570
|
-
* Structural type queries, allow name to be unknown; same with finite hash keys,
|
607
|
+
* Structural type queries, allow name to be unknown; same with finite hash keys,
|
608
|
+
same with generic base types?
|
571
609
|
|
572
610
|
* Allow ... in named args list in queries
|
573
611
|
|
574
612
|
* Queries, include more regexp operators aside from . and ...
|
613
|
+
|
614
|
+
* Queries, allow regexp in class and method names; suggested by Andreas Adamcik, Vienna
|