I'm just starting to play around with the sorbet gem. I have a method that expects and returns an array of objects. The thing is, the length of the array is varied. How can I type check the method? I keep getting Expected type [Object], got array of size 2
Here's my method
sig { params(foo: Array[Object]).returns(Array[Object]) }
def bar(foo)
# code to modify some of the attributes
end
tl;dr You have a syntax error. Use T::Array[Object] (not Array[Object]).
You're using incorrect type syntax for arrays:
# typed: strict
extend T::Sig
sig { params(foo: Array[Object]).returns(Array[Object]) }
def bar(foo)
# code to modify some of the attributes
end
→ View on sorbet.run
The errors show:
editor.rb:5: Use T::Array[...], not Array[...] to declare a typed Array https://srb.help/5026
5 |sig { params(foo: Array[Object]).returns(Array[Object]) }
^^^^^^^^^^^^^
Note:
Array[...] will raise at runtime because this generic was defined in the standard library
Autocorrect: Use `-a` to autocorrect
editor.rb:5: Replace with T::Array
5 |sig { params(foo: Array[Object]).returns(Array[Object]) }
^^^^^
editor.rb:5: Use T::Array[...], not Array[...] to declare a typed Array https://srb.help/5026
5 |sig { params(foo: Array[Object]).returns(Array[Object]) }
^^^^^^^^^^^^^
Note:
Array[...] will raise at runtime because this generic was defined in the standard library
Autocorrect: Use `-a` to autocorrect
editor.rb:5: Replace with T::Array
5 |sig { params(foo: Array[Object]).returns(Array[Object]) }
^^^^^
Errors: 2
Why is it like this?
The [] method on Array has special meaning, but Sorbet uses [] for generic type arguments. To get around that, Sorbet uses the T:: namespace for certain generic classes in the standard library:
→ https://sorbet.org/docs/stdlib-generics
What's happening in your case is that this code:
Array[Object]
is equivalent to having written this:
[Object]
(i.e., "make an array of length one containing the value Object"). [Object] in Sorbet happens to be the way to express a 1-tuple.
Related
I have the following code that you can just copy&paste locally and run it as a single file:
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'sorbet-runtime'
end
require 'sorbet-runtime'
class Numbers
extend T::Sig
sig { params(a: Integer, b: Integer).returns(Integer) }
def sum_of_two(a, b)
a + b
end
sig { params(values: T::Array[Integer]).returns(Integer) }
def sum_of_array(values)
values.sum
end
end
If I add the following lines at the end of the file I'll get the following results
Numbers.new.sum_of_two(1, 2) # => does not fail because the actual args are ints
# Numbers.new.sum_of_two(1.0, 2) # => raises errors because one of the param is Float
Numbers.new.sum_of_array([1, 2, 3]) #=> ok, because everything matches
Numbers.new.sum_of_array([1.0, 2, 3]) #=> raises only an error when tries to match the return type
Please, could anybody explain to me why it behaves like this? Why does not it verify a type in runtime if the type is an array?
This exact situation is discussed in the Documentation:
Sorbet completely erases generic type arguments at runtime. When Sorbet sees a signature like T::Array[Integer], at runtime it will only check whether an argument has class Array, but not whether every element of that array is also an Integer at runtime.
If the return type of a method is not stated, is it still possible to get the inferred return type to be used in a macro?
class Record
def explicit : String
"name"
end
def inferred
["a", "b"]
end
end
# The following works:
puts {{Record.methods.find { |m| m.name.id == "explicit".id }.return_type }}
# The following does not (because .return_type
# is useful when the method explicitly states the return type):
puts {{Record.methods.find { |m| m.name.id == "inferred".id }.return_type }}
No, macros run before any type inference is done and it's impossible to access inferred types from macros. This is because macros have to be fully expanded before the type checker can correctly infer return types.
I'm new to Swift, and have a Ruby background. I'm wondering if there's a way I can do something like this:
def completion_handler response, &block
yield response
end
extracted = proc {|r| puts r}
completion_handler "response", &extracted
response
=> nil
I'm having a little trouble searching as yield mostly brings up sequence generators and I don't think that's what I'm looking for right now. I mostly want to know if this is possible because there's times I like to be able to extract a block to a proc object. In Swift, a closure looks like a block to me. Is there a similar extraction to a proc object?
Swift closures mostly closely resemble Ruby Lambdas. They are objects with a defined set of arguments which return to their calling scope (unlike non-object Ruby Blocks or Ruby Procs which do not check argument count and where a return can escape the calling scope).
As such there's no real yield equivalent. Swift functions which accept a closure argument call the closure explicitly with a parenthetical argument list.
It's possible to save block (or 'closure' as in swift programming guide) in variables or pass them around. A big difference to ruby is that some types have to specified to compile.
Example code (only tested with swift 2.2-dev linux):
func foo(arg1: Int, block: ((Int)->(String))? = nil) {
if let block = block {
// ruby's yield (call the block with argument and get its return value)
// is not part of swift grammar, so we have to call it manually.
print(block(arg1))
}
}
// named closure
let block1 = {(a: Int)->String in
print("yielded", a)
return "returned from block1"
}
foo(1, block: block1)
// unnamed closure
foo(2, block: {
print("yielded", $0)
return "returned from block2"
})
// unnamed closure in another syntax. usable only when the closure is the last argument.
foo(3) {
print("yielded", $0)
return "returned from block3"
}
Output:
yielded 1
returned from block1
yielded 2
returned from block2
yielded 3
returned from block3
In ruby, you can call the following code for printing "foo" in the console:
p "foo"
or
puts "foo"
And it prints "foo". In scala, I created this function:
def p(forPrint: Any): Unit = {
println(forPrint)
}
but it needs to be called like p("foo"). How can I write:
puts "foo"
and print in scala with such syntax?
Or, like if Array.map{} equal Array map{}
can i
"foo" p equal println("foo")
Given this method:
def p(forPrint: Any): Unit = println(forPrint)
It is not possible in Scala to call it like this:
p "foo"
You can only omit the parentheses when calling a method from an object. For example:
scala> println "foo" // doesn't work
<console>:1: error: ';' expected but string literal found.
println "foo"
^
scala> Console println "foo" // does work
foo
In the context of your example, you can place p in an object:
object Test {
def p(forPrint: Any): Unit = println(forPrint)
}
Test p "foo"
The reason you can call array toList, instead of array.toList, is that array is an object, with a method toList. p is a method with an argument "foo", which is not allowed without referencing an object explicitly.
Ruby's enforcement of brackets is extremely lax compared to other programming languages, so it has a syntax that's unusually sparse. This is why it's often a language of choice for writing configuration files, there's not much grit in the expression.
Technically p(x) is equivalent to p x in Ruby, but the same is not true in other languages.
As far as I can tell, you can't do this in Scala because the meaning is different.
Generally trying to force one language to behave like another creates a lot of unnecessary confusion. I'd do it the Scala way whenever possible.
I'm using a rake task to receive and handle data.
The data looks like "code:value" where each code maps to a specific action.
For example, "0xFE:0x47" calls the method corresponding to the 0xFE tag with the parameter 0x47.
For scalability purposes I think this should be mapped to an hash and have the methods defined below:
tags = Hash[0xFA => taskA, 0xFB => taskB, 0xFC => taskC]
def taskA(value)
...
end
def taskB(value)
...
end
def taskC(value)
...
end
then, when a message is received, do a split and call the method on the hash, like:
tokens = message.split(':')
tags[tokens[0]](tokens[1])
Ruby doesn't like the Hash initialization. What's the correct way to solve this problem?
Maybe you're expecting the methods to work like they do in JavaScript, where they're just references until called, but this is not the case. The best approach is to keep them as symbols and then use the send method to call them:
# Define a mapping table between token and method to call
tags = {
0xFA => :taskA,
0xFB => :taskB,
0xFC => :taskC
}
tokens = message.split(/:/)
# Call the method and pass through the value
send(tags[tokens[0]], tokens[1])
The Hash[] initializer is usually reserved for special cases, such as when converting an Array into a Hash. In this case it's redundant if not confusing so is best omitted. { ... } has the effect of creating a Hash implicitly.
Define "doesn't like".
If you try to initialize the hash using function names:
Before the function is defined, you'll get an undefined symbol.
After the function is defined, you're just executing the function.
If you want to map values to functions you can call, consider defining the methods as Procs/lambdas:
> taskA = lambda { |value| puts "foo #{value}" }
> h = { 0x42 => taskA }
> h[0x42].call("bar")
foo bar
You could store symbols, too, but I prefer to use known-existing artifacts, like a variable, so an IDE can help me make sure I'm doing things as right as it can know–symbols are arbitrary and there's no way to make sure they align with an existing method other than trying to call it.