Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: cb9a2930fb36
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 96efa7dba513
Choose a head ref
  • 2 commits
  • 7 files changed
  • 1 contributor

Commits on Jun 6, 2016

  1. Make the T in a Proc accessible as a tuple

    Ary Borenszweig committed Jun 6, 2016
    Copy the full SHA
    5bf717e View commit details
  2. Added Proc#partial (partial application)

    Ary Borenszweig committed Jun 6, 2016
    9
    Copy the full SHA
    96efa7d View commit details
12 changes: 12 additions & 0 deletions spec/compiler/codegen/fun_spec.cr
Original file line number Diff line number Diff line change
@@ -687,4 +687,16 @@ describe "Code gen: fun" do
end
)).to_i.should eq(42)
end

it "accesses T in macros as a TupleLiteral" do
run(%(
struct Proc
def t
{{ T.class_name }}
end
end
->(x : Int32) { 'a' }.t
)).to_string.should eq("TupleLiteral")
end
end
12 changes: 12 additions & 0 deletions spec/compiler/type_inference/fun_spec.cr
Original file line number Diff line number Diff line change
@@ -807,4 +807,16 @@ describe "Type inference: fun" do
Proc(Int32, Void).new { 1 }
)) { |mod| fun_of(int32, mod.nil) }
end

it "accesses T" do
assert_type(%(
struct Proc
def t
T
end
end
->(x : Int32) { 'a' }.t
)) { tuple_of([int32, char]).metaclass }
end
end
22 changes: 22 additions & 0 deletions spec/std/proc_spec.cr
Original file line number Diff line number Diff line change
@@ -65,4 +65,26 @@ describe "Proc" do
func = ->{ 1 }
func.clone.should eq(func)
end

{% if Crystal::VERSION == "0.18.0" %}
it "#partial" do
f = ->(x : Int32, y : Int32, z : Int32) { x + y + z }
f.call(1, 2, 3).should eq(6)

f2 = f.partial(10)
f2.call(2, 3).should eq(15)
f2.call(2, 10).should eq(22)

f3 = f2.partial(20)
f3.call(3).should eq(33)
f3.call(10).should eq(40)

f = ->(x : String, y : Char) { x.index(y) }
f.call("foo", 'o').should eq(1)

f2 = f.partial("bar")
f2.call('a').should eq(1)
f2.call('r').should eq(2)
end
{% end %}
end
30 changes: 18 additions & 12 deletions src/compiler/crystal/macros/macros.cr
Original file line number Diff line number Diff line change
@@ -508,23 +508,29 @@ module Crystal
when Const
matched_type.value
when Type
# The T of a tuple, named tuple or union produce tuple literals
# If it's the T of a variadic generic type, produce tuple literals
# or named tuple literals. The compiler has them as a type
# (a tuple type, or a named tuple type) but the user should see
# them as literals, and having them as a type doesn't add
# any useful information.
if node.single?("T")
instance_type = @type_lookup.instance_type
case instance_type
when TupleInstanceType
return TupleLiteral.map(instance_type.tuple_types) { |t| TypeNode.new(t) }
when NamedTupleInstanceType
entries = instance_type.entries.map do |entry|
NamedTupleLiteral::Entry.new(entry.name, TypeNode.new(entry.type))
type_lookup = @type_lookup.instance_type
if node.names.size == 1
union_type = type_lookup.is_a?(UnionType) && node.names.first == "T"
generic_variadic = type_lookup.is_a?(GenericClassInstanceType) &&
(type_lookup.variadic || type_lookup.double_variadic) &&
type_lookup.type_vars.first_key == node.names.first
if generic_variadic || union_type
case matched_type
when TupleInstanceType
return TupleLiteral.map(matched_type.tuple_types) { |t| TypeNode.new(t) }
when NamedTupleInstanceType
entries = matched_type.entries.map do |entry|
NamedTupleLiteral::Entry.new(entry.name, TypeNode.new(entry.type))
end
return NamedTupleLiteral.new(entries)
when UnionType
return TupleLiteral.map(matched_type.union_types) { |t| TypeNode.new(t) }
end
return NamedTupleLiteral.new(entries)
when UnionType
return TupleLiteral.map(instance_type.union_types) { |t| TypeNode.new(t) }
end
end

14 changes: 12 additions & 2 deletions src/compiler/crystal/semantic/method_lookup.cr
Original file line number Diff line number Diff line change
@@ -84,7 +84,12 @@ module Crystal
my_parents.each do |parent|
break unless parent.is_a?(IncludedGenericModule) || parent.module?

matches = parent.lookup_matches_with_modules(signature, owner, parent, matches_array)
# If this is a generic instance type and our parent is the generic class, use
# this type as the type lookup (so we can find type arguments)
type_lookup = parent
type_lookup = self if self.is_a?(GenericClassInstanceType) && type_lookup == self.generic_class

matches = parent.lookup_matches_with_modules(signature, owner, type_lookup, matches_array)
return matches unless matches.empty?
end
end
@@ -114,7 +119,12 @@ module Crystal
# and can be known by invoking `lookup_new_in_ancestors?`
if my_parents && !(!lookup_new_in_ancestors? && is_new)
my_parents.each do |parent|
matches = parent.lookup_matches(signature, owner, parent, matches_array)
# If this is a generic instance type and our parent is the generic class, use
# this type as the type lookup (so we can find type arguments)
type_lookup = parent
type_lookup = self if self.is_a?(GenericClassInstanceType) && type_lookup == self.generic_class

matches = parent.lookup_matches(signature, owner, type_lookup, matches_array)
if matches.cover_all?
return matches
else
4 changes: 3 additions & 1 deletion src/compiler/crystal/types.cr
Original file line number Diff line number Diff line change
@@ -1663,6 +1663,8 @@ module Crystal
delegate type_desc, @generic_class
delegate container, @generic_class
delegate lookup_new_in_ancestors?, @generic_class
delegate variadic, @generic_class
delegate double_variadic, @generic_class

def declare_instance_var(name, type_vars : Array(TypeVar))
type = solve_type_vars(type_vars)
@@ -3127,7 +3129,7 @@ module Crystal
getter fun_types : Array(Type)

def initialize(@program, @fun_types)
var = Var.new("T", self)
var = Var.new("T", @program.tuple_of(fun_types))
var.bind_to var
super(program, program.proc, {"T" => var} of String => ASTNode)

33 changes: 33 additions & 0 deletions src/proc.cr
Original file line number Diff line number Diff line change
@@ -95,6 +95,39 @@ struct Proc
ptr.value
end

# Returns a new Proc that has its first arguments fixed to the values given by *args*.
#
# See [Wikipedia, Partial application](https://en.wikipedia.org/wiki/Partial_application)
#
# ```
# add = ->(x : Int32, y : Int32) { x + y }
# add.call(1, 2) # => 3
#
# add_one = add.partial(1)
# add_one.call(2) # => 3
# add_one.call(10) # => 11
#
# add_one_and_two = add_one.partial(2)
# add_one_and_two.call # => 3
# ```
def partial(*args : *U)
{% begin %}
{% remaining = (T.size - 1 - U.size) %}
->(
{% for i in 0...remaining %}
arg{{i}} : {{T[i + U.size]}},
{% end %}
) {
call(
*args,
{% for i in 0...remaining %}
arg{{i}},
{% end %}
)
}
{% end %}
end

def pointer
internal_representation[0]
end