typed.rb 0.0.16 → 0.0.17

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ac43557e5bdf48bfa8082e5526b798e7a2fc3aca
4
- data.tar.gz: ed3a52ee31070c01b9dff44e8decd67bbabac035
3
+ metadata.gz: f2c8f6d09d1aaaa16400b98c8303b0f33d0776e5
4
+ data.tar.gz: b7a8374f668d3af8aba103fe87f5ba2b9224ec4c
5
5
  SHA512:
6
- metadata.gz: 06c43f6cc812fa5b98a53e4d5e556fce63492746241f618ead568a04bcb018ec83a239891484168b692927699eb4fc7fa8793bf514cb3bf2bbcce790ee902e3f
7
- data.tar.gz: 9fa398a80e4c0e57807df2ae73538176e2ffb1d9b1c2387a3c4aba0a28e43898df5c82fc0ce715f9318c3f249fecffeab4529792981676b7c109cf2f06c56868
6
+ metadata.gz: a003f890681c58cb00c8be8ca3ee3f976a7cfaecd38df6d671119e29b76853c82724427af5b0b0a2975b18a20c13df0203ec95c470ef6ce5c51ed073aa37f893
7
+ data.tar.gz: 415c505025c2dedca743ccd46b62ddc16a719570d599b40ac3824b63713adab342c46f5f3abd2a17328a2c1832609074a05034a0e6d5c3a671522b7d5f0aeb7d
data/README.md CHANGED
@@ -139,6 +139,47 @@ type TypeName[T]? (super BaseType[U]*)?
139
139
  - ```[T]*``` type variables for the polymorphic type
140
140
  - ```(super BaseType[U]*)?``` base polymorphic class/module for the type
141
141
 
142
+ ## Polymorphism
143
+
144
+ Typed.rb supports annotations for polymorphic types and methods. Type variables are introduced using square brackets and a capital letter (e.g ```[T]```).
145
+ When creating an instance of a polymorphic type in your code, information about the specialization of the type variables must be provided. The Typed.rb runtime defines a version of the ```#call`` method for the ```Class``` class as a noop that can be used to pass the value of the type variables.
146
+ For example, to declare an ```Array``` of ```Integers``` the following code can be used:
147
+
148
+ ```ruby
149
+ xs = Array.(Integer).new
150
+ ```
151
+ Instead of a class, a string with the description of the type variables using the same syntax as the one used in the type annotations can be used.
152
+ The previous definition could be expressed in an equivalent way using the following snippet:
153
+
154
+ ```ruby
155
+ xs = Array.('Integer').new
156
+ ```
157
+
158
+ Using a string is the only option when dealing with type vars as in the following declaration:
159
+
160
+ ```ruby
161
+ ts 'type Wrapper[T]'
162
+ class Wrapper
163
+
164
+ ts '#zero / -> Array[T]'
165
+ def zero
166
+ Array.('[T]').new
167
+ end
168
+
169
+ end
170
+ ```
171
+
172
+ If no value for the type variables of the polymorphic type are given, the type checker will assume that ```Object``` is used.
173
+
174
+ When initiating objects using type literals, like arrays or hashes, the type checker will try to infer the type variables using the max type for the provided values in the literal:
175
+
176
+ ```ruby
177
+ [1, 2, 3] # Array[Integer]
178
+ [1, :2, '3'] # Array[Object]
179
+ ```
180
+
181
+ Type boundaries can be provided for the type variables using the ```?```, ```<``` and ```>``` symbols, where ```?``` is used as type wildcard, for instance: ```[? < Integer]```, ```[? > String]```. ```[?]```.
182
+
142
183
 
143
184
  ## Type inference and minimal typing
144
185
 
Binary file
@@ -62,6 +62,7 @@ module TypedRb
62
62
  def valid_super_type?(base_class, super_type_info)
63
63
  return false if super_type_info.nil?
64
64
  valid = base_class.ancestors.map(&:name).detect { |klass_name| klass_name == super_type_info[:type].to_s }
65
+ valid = valid || base_class.meta_ancestors.map(&:name).detect { |klass_name| klass_name == super_type_info[:type].to_s }
65
66
  return true if valid
66
67
  fail ::TypedRb::Types::TypeParsingError,
67
68
  "Super type annotation '#{super_type_info[:type]}' not a super class of '#{base_class}'"
data/lib/typed/runtime.rb CHANGED
@@ -30,7 +30,7 @@ class BasicObject
30
30
  end
31
31
 
32
32
  class Class
33
- ts '.call / Class... -> unit'
33
+ ts '.call / Object... -> Class'
34
34
  def call(*_types)
35
35
  self
36
36
  end
@@ -0,0 +1,114 @@
1
+ module TypedRb
2
+ module Types
3
+ module Polymorphism
4
+ module GenericObject
5
+ def generic?
6
+ true
7
+ end
8
+
9
+ def generic_singleton_object
10
+ @generic_singleton_object ||= BasicObject::TypeRegistry.find_generic_type(ruby_type)
11
+ end
12
+
13
+ def ancestor_of_super_type?(super_type_klasses, function_klass_type)
14
+ super_type_klasses.detect do |super_type_klass|
15
+ super_type_klass.ruby_type.ancestors.include?(function_klass_type)
16
+ end
17
+ end
18
+
19
+ def materialize_super_type_found_function(message, num_args, block,
20
+ super_type,
21
+ super_type_vars)
22
+ super_type_materialization_args = parse_super_type_materialization_args(super_type_vars)
23
+ # we build the concrete type for the arguments based in the subclass bindings and the
24
+ # super type parsed value
25
+ materialized_super_type_in_context = super_type.materialize(super_type_materialization_args).type_vars(recursive: false)
26
+ # Now we check if the parsed type is valid provided the constraints of the super class
27
+ super_type_generic_object = BasicObject::TypeRegistry.find_generic_type(super_type.ruby_type)
28
+ materialized_super_type = super_type_generic_object.materialize(materialized_super_type_in_context)
29
+
30
+ # materialized_super_type.type_vars = super_type.type_vars # ...
31
+ materialized_super_type.as_object_type.find_function_type(message, num_args, block)
32
+ end
33
+
34
+ def parse_super_type_materialization_args(super_type_vars)
35
+ super_type_vars.map do |super_type_var|
36
+ parse_super_type_materialization_arg(super_type_var)
37
+ end
38
+ end
39
+
40
+ def parse_super_type_materialization_arg(super_type_var)
41
+ return super_type_var if super_type_var.bound
42
+ found_matching_var = type_vars.detect do |var|
43
+ var_name = var.name.split(':').last
44
+ super_type_var.name.index(var_name)
45
+ end
46
+ if found_matching_var
47
+ base_matching_var = found_matching_var.dup
48
+ base_matching_var.name = super_type_var.name
49
+ base_matching_var.variable = super_type_var.variable
50
+ base_matching_var
51
+ else
52
+ fail TypedRb::TypeCheckError,
53
+ "Error materializing super type annotation for variable #{generic_singleton_object.ruby_type} '#{super_type_var.split(':').last}' not found in base class #{ruby_type}"
54
+ end
55
+ end
56
+
57
+ def materialize_found_function(function_type)
58
+ return function_type unless function_type.generic?
59
+ from_args = function_type.from.map { |arg| materialize_found_function_arg(arg) }
60
+ to_arg = materialize_found_function_arg(function_type.to)
61
+ if function_type.block_type
62
+ materialized_block_type = materialize_found_function(function_type.block_type)
63
+ end
64
+
65
+ generic_function = (from_args + [to_arg, materialized_block_type]).any? do |arg|
66
+ arg.is_a?(Polymorphism::TypeVariable) ||
67
+ (arg.respond_to?(:generic?) && arg.generic?)
68
+ end
69
+
70
+ if generic_function
71
+ materialized_function = TyGenericFunction.new(from_args, to_arg, function_type.parameters_info, node)
72
+ materialized_function.local_typing_context = function_type.local_typing_context
73
+ else
74
+ materialized_function = TyFunction.new(from_args, to_arg, function_type.parameters_info, node)
75
+ end
76
+
77
+ materialized_function.with_block_type(materialized_block_type)
78
+ end
79
+
80
+ def materialize_found_function_arg(arg)
81
+ if arg.is_a?(Polymorphism::TypeVariable)
82
+ matching_var = generic_type_var_to_applied_type_var(arg)
83
+
84
+ # if matching_var && matching_var.wildcard? && matching_var.lower_bound
85
+ # matching_var.lower_bound
86
+ # elsif matching_var
87
+ # WILDCARD
88
+ if matching_var
89
+ # Type variables and generic methods => function will still be generic
90
+ (matching_var.is_a?(Polymorphism::TypeVariable) && matching_var.bound) || matching_var
91
+ else
92
+ # generic_function = true
93
+ # TyUnboundType.new(matching_var.variable, :lower_bound)
94
+ # fail TypeCheckError, "Cannot find matching type var for #{arg.variable} instantiating #{self}", arg.node
95
+ # method generic var?
96
+ arg
97
+ end
98
+ elsif arg.is_a?(TyGenericSingletonObject)
99
+ arg.materialize_with_type_vars(type_vars, :lower_bound).as_object_type
100
+ else
101
+ arg
102
+ end
103
+ end
104
+
105
+ def generic_type_var_to_applied_type_var(type_var)
106
+ i = TypeRegistry.find_generic_type(ruby_type).type_vars.find_index { |generic_type_var| generic_type_var.variable == type_var.variable }
107
+ i && type_vars[i]
108
+ end
109
+
110
+ end
111
+ end
112
+ end
113
+ end
114
+
@@ -1,9 +1,12 @@
1
1
  require_relative 'ty_object'
2
2
  require_relative 'polymorphism/generic_comparisons'
3
3
  require_relative 'polymorphism/generic_variables'
4
+ require_relative 'polymorphism/generic_object'
5
+
4
6
  module TypedRb
5
7
  module Types
6
8
  class TyGenericObject < TyObject
9
+ include Polymorphism::GenericObject
7
10
  include Polymorphism::GenericComparisons
8
11
  include Polymorphism::GenericVariables
9
12
 
@@ -34,102 +37,6 @@ module TypedRb
34
37
  end
35
38
  end
36
39
 
37
- def generic?
38
- true
39
- end
40
-
41
- def materialize_found_function(function_type)
42
- return function_type unless function_type.generic?
43
- from_args = function_type.from.map { |arg| materialize_found_function_arg(arg) }
44
- to_arg = materialize_found_function_arg(function_type.to)
45
- if function_type.block_type
46
- materialized_block_type = materialize_found_function(function_type.block_type)
47
- end
48
-
49
- generic_function = (from_args + [to_arg, materialized_block_type]).any? do |arg|
50
- arg.is_a?(Polymorphism::TypeVariable) ||
51
- (arg.respond_to?(:generic?) && arg.generic?)
52
- end
53
-
54
- if generic_function
55
- materialized_function = TyGenericFunction.new(from_args, to_arg, function_type.parameters_info, node)
56
- materialized_function.local_typing_context = function_type.local_typing_context
57
- else
58
- materialized_function = TyFunction.new(from_args, to_arg, function_type.parameters_info, node)
59
- end
60
-
61
- materialized_function.with_block_type(materialized_block_type)
62
- end
63
-
64
- def materialize_super_type_found_function(message, num_args, block,
65
- super_type,
66
- super_type_vars)
67
- super_type_materialization_args = parse_super_type_materialization_args(super_type_vars)
68
- # we build the concrete type for the arguments based in the subclass bindings and the
69
- # super type parsed value
70
- materialized_super_type_in_context = super_type.materialize(super_type_materialization_args).type_vars(recursive: false)
71
- # Now we check if the parsed type is valid provided the constraints of the super class
72
- super_type_generic_object = BasicObject::TypeRegistry.find_generic_type(super_type.ruby_type)
73
- materialized_super_type = super_type_generic_object.materialize(materialized_super_type_in_context)
74
-
75
- # materialized_super_type.type_vars = super_type.type_vars # ...
76
- materialized_super_type.as_object_type.find_function_type(message, num_args, block)
77
- end
78
-
79
- def parse_super_type_materialization_args(super_type_vars)
80
- super_type_vars.map do |super_type_var|
81
- parse_super_type_materialization_arg(super_type_var)
82
- end
83
- end
84
-
85
- def parse_super_type_materialization_arg(super_type_var)
86
- return super_type_var if super_type_var.bound
87
- found_matching_var = type_vars.detect do |var|
88
- var_name = var.name.split(':').last
89
- super_type_var.name.index(var_name)
90
- end
91
- if found_matching_var
92
- base_matching_var = found_matching_var.dup
93
- base_matching_var.name = super_type_var.name
94
- base_matching_var.variable = super_type_var.variable
95
- base_matching_var
96
- else
97
- fail TypedRb::TypeCheckError,
98
- "Error materializing super type annotation for variable #{generic_singleton_object.ruby_type} '#{super_type_var.split(':').last}' not found in base class #{ruby_type}"
99
- end
100
- end
101
-
102
- def ancestor_of_super_type?(super_type_klasses, function_klass_type)
103
- super_type_klasses.detect do |super_type_klass|
104
- super_type_klass.ruby_type.ancestors.include?(function_klass_type)
105
- end
106
- end
107
-
108
- def materialize_found_function_arg(arg)
109
- if arg.is_a?(Polymorphism::TypeVariable)
110
- matching_var = generic_type_var_to_applied_type_var(arg)
111
-
112
- # if matching_var && matching_var.wildcard? && matching_var.lower_bound
113
- # matching_var.lower_bound
114
- # elsif matching_var
115
- # WILDCARD
116
- if matching_var
117
- # Type variables and generic methods => function will still be generic
118
- (matching_var.is_a?(Polymorphism::TypeVariable) && matching_var.bound) || matching_var
119
- else
120
- # generic_function = true
121
- # TyUnboundType.new(matching_var.variable, :lower_bound)
122
- # fail TypeCheckError, "Cannot find matching type var for #{arg.variable} instantiating #{self}", arg.node
123
- # method generic var?
124
- arg
125
- end
126
- elsif arg.is_a?(TyGenericSingletonObject)
127
- arg.materialize_with_type_vars(type_vars, :lower_bound).as_object_type
128
- else
129
- arg
130
- end
131
- end
132
-
133
40
  def to_s
134
41
  base_string = super
135
42
  var_types_strings = @type_vars.map do |var_type|
@@ -168,14 +75,6 @@ module TypedRb
168
75
  self
169
76
  end
170
77
 
171
- def generic_singleton_object
172
- @generic_singleton_object ||= BasicObject::TypeRegistry.find_generic_type(ruby_type)
173
- end
174
-
175
- def generic_type_var_to_applied_type_var(type_var)
176
- i = TypeRegistry.find_generic_type(ruby_type).type_vars.find_index { |generic_type_var| generic_type_var.variable == type_var.variable }
177
- i && type_vars[i]
178
- end
179
78
  end
180
79
  end
181
80
  end
@@ -1,10 +1,12 @@
1
1
  require_relative 'ty_singleton_object'
2
2
  require_relative 'polymorphism/generic_comparisons'
3
3
  require_relative 'polymorphism/generic_variables'
4
+ require_relative 'polymorphism/generic_object'
4
5
 
5
6
  module TypedRb
6
7
  module Types
7
8
  class TyGenericSingletonObject < TySingletonObject
9
+ include Polymorphism::GenericObject
8
10
  include Polymorphism::GenericComparisons
9
11
  include Polymorphism::GenericVariables
10
12
 
@@ -111,10 +113,6 @@ module TypedRb
111
113
  Model::TmClass.with_fresh_bindings(self, nil, node)
112
114
  end
113
115
 
114
- def generic?
115
- true
116
- end
117
-
118
116
  def apply_bindings(bindings_map)
119
117
  type_vars(recursive: false).each_with_index do |var, _i|
120
118
  if var.is_a?(Polymorphism::TypeVariable) && var.bound_to_generic?
@@ -168,6 +166,28 @@ module TypedRb
168
166
  self.class.new(ruby_type, materialized_type_vars, super_type, node)
169
167
  end
170
168
 
169
+ # This object has concrete type parameters
170
+ # The generic Function we retrieve from the registry might be generic
171
+ # If it is generic we apply the bound parameters and we obtain a concrete function type
172
+ def find_function_type(message, num_args, block)
173
+ function_klass_type, function_type = super(message, num_args, block)
174
+ if function_klass_type != ruby_type && ancestor_of_super_type?(generic_singleton_object.super_type, function_klass_type)
175
+ target_class = ancestor_of_super_type?(generic_singleton_object.super_type, function_klass_type)
176
+ TypedRb.log binding, :debug, "Found message '#{message}', generic function: #{function_type}, explicit super type #{target_class}"
177
+ target_type_vars = target_class.type_vars
178
+ materialize_super_type_found_function(message, num_args, block, target_class, target_type_vars)
179
+ elsif function_klass_type != ruby_type && BasicObject::TypeRegistry.find_generic_type(function_klass_type)
180
+ TypedRb.log binding, :debug, "Found message '#{message}', generic function: #{function_type}, implict super type #{function_klass_type}"
181
+ target_class = BasicObject::TypeRegistry.find_generic_type(function_klass_type)
182
+ materialize_super_type_found_function(message, num_args, block, target_class, type_vars)
183
+ else
184
+ TypedRb.log binding, :debug, "Found message '#{message}', generic function: #{function_type}"
185
+ materialized_function = materialize_found_function(function_type)
186
+ TypedRb.log binding, :debug, "Found message '#{message}', materialized generic function: #{materialized_function}"
187
+ [function_klass_type, materialized_function]
188
+ end
189
+ end
190
+
171
191
  protected
172
192
 
173
193
  def apply_type_arguments(fresh_vars_generic_type, actual_arguments)
data/lib/typed/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module TypedRb
2
2
  unless defined?(VERSION)
3
- VERSION = '0.0.16'
3
+ VERSION = '0.0.17'
4
4
  end
5
5
  end
@@ -0,0 +1,39 @@
1
+ require 'typed/runtime'
2
+
3
+ module Monoid
4
+
5
+ ts 'type Monoid::Instance[T]'
6
+ module Instance
7
+ ts '#mappend / [T] -> [T]'
8
+ abstract(:mappend)
9
+ end
10
+
11
+ ts 'type Monoid::Class[T]'
12
+ module Class
13
+ ts '#mempty / -> [T]'
14
+ abstract(:mempty)
15
+ end
16
+ end
17
+
18
+ ts 'type Array[T] super Monoid::Instance[Array[T]]'
19
+ ts 'type Array[T] super Monoid::Class[Array[T]]'
20
+ class Array
21
+
22
+ extend Monoid::Class
23
+ include Monoid::Instance
24
+
25
+ def mappend(b)
26
+ concat(b)
27
+ end
28
+
29
+ def self.mempty
30
+ []
31
+ end
32
+
33
+ end
34
+
35
+ a = Array.(String).mempty
36
+
37
+ ->() {
38
+ a.mappend([2])
39
+ }
@@ -16,6 +16,7 @@ module Monoid
16
16
  end
17
17
 
18
18
  ts 'type Array[T] super Monoid::Instance[Array[T]]'
19
+ ts 'type Array[T] super Monoid::Class[Array[T]]'
19
20
  class Array
20
21
 
21
22
  extend Monoid::Class
@@ -31,4 +32,8 @@ class Array
31
32
 
32
33
  end
33
34
 
34
- Array.(Integer).new.mappend([3])
35
+ a = Array.(String).mempty
36
+
37
+ ->() {
38
+ a.mappend(['string'])
39
+ }
@@ -62,7 +62,7 @@ describe TypedRb::Language do
62
62
  end
63
63
  end
64
64
 
65
- context 'with monoid error example, inconsistent type annotation' do
65
+ context 'with monoid error example 1, inconsistent type annotation' do
66
66
  let(:example) { 'monoid/monoid_error1.rb' }
67
67
 
68
68
  it 'should be possible to type check the example correctly' do
@@ -74,7 +74,7 @@ describe TypedRb::Language do
74
74
  end
75
75
  end
76
76
 
77
- context 'with monoid error example, inconsistent type annotation' do
77
+ context 'with monoid error example 2, inconsistent type annotation' do
78
78
  let(:example) { 'monoid/monoid_error2.rb' }
79
79
 
80
80
  it 'should be possible to type check the example correctly' do
@@ -86,7 +86,7 @@ describe TypedRb::Language do
86
86
  end
87
87
  end
88
88
 
89
- context 'with monoid error example, inconsistent type annotation' do
89
+ context 'with monoid error example 3, inconsistent type annotation' do
90
90
  let(:example) { 'monoid/monoid_error3.rb' }
91
91
 
92
92
  it 'should be possible to type check the example correctly' do
@@ -98,7 +98,7 @@ describe TypedRb::Language do
98
98
  end
99
99
  end
100
100
 
101
- context 'with monoid error example, inconsistent type annotation' do
101
+ context 'with monoid error example 4, inconsistent type annotation' do
102
102
  let(:example) { 'monoid/monoid_error4.rb' }
103
103
 
104
104
  it 'should be possible to type check the example correctly' do
@@ -121,4 +121,16 @@ describe TypedRb::Language do
121
121
  }.to_not raise_error
122
122
  end
123
123
  end
124
+
125
+ context 'with monoid2 error example 1, inconsistent type' do
126
+ let(:example) { 'monoid2/monoid2_error1.rb' }
127
+
128
+ it 'should be possible to type check the example correctly' do
129
+ expect {
130
+ silence_stream(STDOUT) do
131
+ language.check_file(file, true)
132
+ end
133
+ }.to raise_error(TypedRb::Types::UncomparableTypes)
134
+ end
135
+ end
124
136
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typed.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.16
4
+ version: 0.0.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Antonio Garrote
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-27 00:00:00.000000000 Z
11
+ date: 2016-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -124,6 +124,7 @@ files:
124
124
  - lib/typed/types.rb
125
125
  - lib/typed/types/polymorphism/existential_type_variable.rb
126
126
  - lib/typed/types/polymorphism/generic_comparisons.rb
127
+ - lib/typed/types/polymorphism/generic_object.rb
127
128
  - lib/typed/types/polymorphism/generic_variables.rb
128
129
  - lib/typed/types/polymorphism/type_variable.rb
129
130
  - lib/typed/types/polymorphism/type_variable_register.rb
@@ -153,6 +154,7 @@ files:
153
154
  - spec/lib/examples/monoid/monoid_error3.rb
154
155
  - spec/lib/examples/monoid/monoid_error4.rb
155
156
  - spec/lib/examples/monoid2.rb
157
+ - spec/lib/examples/monoid2/monoid2_error1.rb
156
158
  - spec/lib/language_spec.rb
157
159
  - spec/lib/model/tm_abs_spec.rb
158
160
  - spec/lib/model/tm_array_literal_spec.rb