I want to extend the standard Array with a new instance method but I keep getting a runtime error about the type_member not being found.
The definition looks like this.
class Array
extend T::Sig
extend T::Generic
sig do
type_parameters(:U)
.params(split_elem: T.type_parameter(:U))
.returns(T::Array[Elem])
end
def split_on(split_elem)
output = []
group = []
each do |elem|
if elem.eql?(split_elem)
output << group unless group.empty?
group = []
else
group << elem
end
end
output << group unless group.empty?
output
end
end
Is there a way to explicitly require the rbi file declaring Elem?
When trying to run the code, I get the following error. I have tried requiring the sorbet-runtime but no success so far.
NameError: uninitialized constant Array::Elem
.returns(T::Array[Elem])
RBI files are purely static artifacts, they are not meant to be required or run in any way. So, the high-level answer to your question is "no, you can't require RBI files".
The problem you are facing is that you are adding a signature that is statically checkable (i.e. Sorbet can understand Elem and type-check your code without running it), but it is not valid at runtime, since there is no actual Elem constant under the Ruby Array class.
There are three ways you can square this circle:
You want to actually return the same type of the receiver, in which case, you can just use T.self_type as the return type, which will solve your problem with Elem. Docs for T.self_type are here.
You can move the inline signature for Array#split_on to an RBI file, which will make the signature checked only statically (based on what I said about RBI files above), or
You can use T::Sig::WithoutRuntime.sig instead of sig to write your signature, as explained here.
Related
Given the following code:
# typed: strict
extend T::Sig
sig { params(x: T::Hash[String,String]).returns(NilClass) }
def foo(x); end
foo("foo" => 123)
Playground Link
I would expect Sorbet to recognize that the passed hash has the wrong value parameter. How can I properly declare a signature which will ensure the hash passed only contains allowed values types?
Unfortunately, this is a known issue in Sorbet
https://github.com/sorbet/sorbet/issues/713
You're doing everything right, and Sorbet let you down. Feel free to follow the issue, but we estimate that this particular bug will be quite hard to fix, because of some other constraints we're trying to maintain.
I have a method called $muffinize and I would like to find where it can be found in my code. In other words, given the following code:
class A
def foo
$muffinize(1)
end
def bar
...
end
end
class B
def shoop
$muffinize(2)
end
def woop
...
end
end
class C
def nope
...
end
end
I would like to the result to be (written to a file):
A:foo
B:shoop
I was thinking of accomplishing this with a Regex, but I was wondering if there would be some way of accomplishing this with Ruby meta-programming (which I might be accidentally using as a buzz-word)?
Kernel.caller() will help you show the line number and method that is calling it at runtime. If you put something like puts caller(1,1) in your muffinize function it will output those locations, but only if they are called at runtime.
If you want to do offline source analysis, you need to parse the AST (abstract syntax tree) with something like https://github.com/whitequark/parser.
Here is a quick example with ripper (built into new rubies) - this isn't strictly an AST but it's not extracting classes either
#!/usr/local/env ruby
require 'ripper'
#require 'pry'
contents = File.open('example.rb').read
code = Ripper.lex(contents)
code.each do |line|
if(line[1] == :on_ident and line[2] == "muffinize")
puts "muffinize found at line #{line.first.first}"
end
end
Ignoring the fact that your code isn't even syntactically valid, this is simply not possible.
Here's a simple example:
class A
def foo
bar
muffinize(1)
end
end
A#foo will call Object#muffinize if and only if bar terminates. Which means that figuring out whether or not A#foo calls Object#muffinize requires to solve the Halting Problem.
By getting a list of classes and methods via ri, I was then able to analyze each method to retreive their source code using the method_source gem and then searching for muffinize. This does not rule out the possibility of muffinize from appearing in a comment or a string, but I consider the likelihood of this happening to be small enough to ignore.
I've only been learning the deep parts of Ruby for a few months so apologies if this is a bit of a dumb question. I'm attempting to recursively iterate through an Array that may contain nested Arrays and as such I need to check the type of the current element. I have the following code as a small test:
arr = [ 1..2, [3..4, [5..6]], [7..8, [9..10]] ]
arr.each do |node|
p node.class
p node.instance_of? Array
end
When I run it, I get the following output:
Range
false
Array
false
Array
false
I expected the last two to return True, given I have an Array containing a Range and two nested Arrays.
What's even weirder, is if I write the following:
node.class.name == "Array"
It returns True, as it should.
What's happening here?
Ruby Version: MRI 1.9.3-p194
Note: I eventually realised that this was occurring due to the way I namespace my code using modules to avoid code-collision, like so, but also verify object identity in a naughty way:
module MyProg
class MyClass
attr_reader :my_array
def initialize(size)
#my_array = Array.new(size)
end
end
end
MyProg::MyClass.new
Doing this isolates your code but has the downfall of causing all class lookups to be resolved starting from under your namespace. This means that in the above example, my_array.class would actually resolve to MyProg::Array instead of the global Array class.
If you namespace like this and you still want to use this method, you can remedy it by using the double-colon global identifier before the class to force Ruby to begin lookup from the global namespace:
arr.is_a? ::Array
arr.is_a? ::String
Given Ruby's Duck Typing abilities however (and for better code maintenance later on), you should really be testing the behaviour of the object as-per Peter's suggestion below. As such I'm marking his answer as correct for some excellent help given to a learner!
I wrote another answer, but one major question is - why are you doing this? Why not, instead, just call flatten on the array so you just get the entries? Or, check for the behavior of the objects instead. You might need to give more detail about what you require.
You really mean is_a?, which is a more general test to see if the node is of type Array, rather than a specific instance of the specific Array class that you mention. See here for more details. But if you just use is_a? everything will make sense.
I ran your code and got these results.
Range
false
Array
true
Array
true
I'm running ruby 1.9.3p125
It is possible to define a prototype of a function or in some way indicate to Ruby that a function exists even though it may not be defined yet?
I have lots of classes like this:
class Program
FIRST = Block.FIRST
FOLLOW = Set.new['$']
end
class Block
FIRST = Declaration.FIRST
FOLLOW = Set.new['.']
end
class Declaration
FIRST = ConstDecl.FIRST + VarDecl.FIRST + ProcDecl.FIRST
end
class ConstDecl
FIRST = Set.new['const'] + EMPTY_SET
end
Which as you can see reference fields from classes that are defined below them, Is there a way to indicate to Ruby that these classes exist, and ask Ruby to look for them?
The simplest way I can think of is something like this:
class Program
def self.first; Block.first end
def self.follow; Set.new['$'] end
end
class Block
def self.first; Declaration.first end
def self.follow; Set.new['.'] end
end
class Declaration
def self.first; ConstDecl.first + VarDecl.first + ProcDecl.first end
end
class ConstDecl
def self.first; Set.new['const'] + EMPTY_SET end
end
This doesn't seem like good design to me, though, I'd probably make those objects instead of classes and use a proper type hierarchy.
A major difference between Ruby and other languages which you may be accustomed to (like C/C++) is that before execution, a C/C++ program is processed by a compiler which matches up uses of variables/functions to their definitions. Ruby programs are simply executed from top to bottom, one statement at a time. So when a line which references Block.FIRST is executed, the Ruby interpreter can't "look forward" in the program code and see what value will be assigned to Block.FIRST later. It knows nothing about what will come later; it only knows what it has executed so far.
Perhaps one of the strongest characteristics of Ruby is that almost everything is dynamic and can be changed at run-time. If you are coming from a C/C++ background, this is the first thing you need to get your head around to understand Ruby. For example, constants in Ruby can be assigned conditionally:
class Block
if rand % 2 == 0
FIRST = '.'
else
FIRST = '$'
end
end
If the Ruby interpreter was required to "look forward" to see what the value of Block.FIRST should be, what should it predict in the above case?
This is a conceptual shift from what you are used to, and it will require you to structure your programs in a different way, and think about your programs in a different way. If you try to write C/C++/Java in Ruby, you will be fighting all the way.
In this case, I recommend you simply reverse the order of your definitions and go "bottom-up". There are other ways to achieve the same effect, but that is the simplest one.
you can try defined?(function_name)
I would like to do some fairly heavy-duty reflection in Ruby. I want to create a function that returns the names of the arguments of various calling functions higher up the call stack (just one higher would be enough but why stop there?). I could use Kernel.caller, go to the file and parse the argument list but that would be ugly and unreliable.
The function that I would like would work in the following way:
module A
def method1( tuti, fruity)
foo
end
def method2(bim, bam, boom)
foo
end
def foo
print caller_args[1].join(",") #the "1" mean one step up the call stack
end
end
A.method1
#prints "tuti,fruity"
A.method2
#prints "bim, bam, boom"
I would not mind using ParseTree or some similar tool for this task but looking at Parsetree, it is not obvious how to use it for this purpose. Creating a C extension like this is another possibility but it would be nice if someone had already done it for me.
I can see that I'll probably need some kind of C extension. I suppose that means my question is what combination of C extension would work most easily. I don't think caller+ParseTree would be enough by themselves.
As far as why I would like to do this goes, rather than saying "automatic debugging", perhaps I should say that I would like to use this functionality to do automatic checking of the calling and return conditions of functions:
def add x, y
check_positive
return x + y
end
Where check_positive would throw an exception if x and y weren't positive. Obviously, there would be more to it than that but hopefully this gives enough motivation.
In Ruby 1.9.2, you can trivially get the parameter list of any Proc (and thus of course also of any Method or UnboundMethod) with Proc#parameters:
A.instance_method(:method1).parameters # => [[:req, :tuti], [:req, :fruity]]
The format is an array of pairs of symbols: type (required, optional, rest, block) and name.
For the format you want, try
A.instance_method(:method1).parameters.map(&:last).map(&:to_s)
# => ['tuti', 'fruity']
Of course, that still doesn't give you access to the caller, though.
I suggest you take a look at Merb's action-args library.
require 'rubygems'
require 'merb'
include GetArgs
def foo(bar, zed=42)
end
method(:foo).get_args # => [[[:bar], [:zed, 42]], [:zed]]
If you don't want to depend on Merb, you can choose and pick the best parts from the source code in github.
I have a method that is quite expensive and only almost works.
$shadow_stack = []
set_trace_func( lambda {
|event, file, line, id, binding, classname|
if event == "call"
$shadow_stack.push( eval("local_variables", binding) )
elsif event == "return"
$shadow_stack.pop
end
} )
def method1( tuti, fruity )
foo
end
def method2(bim, bam, boom)
foo
x = 10
y = 3
end
def foo
puts $shadow_stack[-2].join(", ")
end
method1(1,2)
method2(3,4,4)
Outputs
tuti, fruity
bim, bam, boom, x, y
I'm curious as to why you'd want such functionality in such a generalized manner.
I'm curious how you think this functionality would allow for automatic debugging? You'd still need to inject calls to your "foo" function. In fact, something based on set_trace_func is more able to be automatic, as you don't need to touch existing code. Indeed this is how debug.rb is implemented, in terms of set_trace_func.
The solutions to your precise question are indeed basically, as you outlined. use caller + parsetree, or open the file and grab the data that way. There is no reflection capability that I am aware of that will let you get the names of arguments. You can approve upon my solution by grabbing the associated method object and calling #arity to then infer what of local_variables are arguments, but though it appears the result of that function is ordered, I'm not sure it is safe to rely on that. If you don't mind me asking, once you have the data and the interface you describe, what are you going to do with it? Automatic debugging was not what initially came to mind when I imagined uses for this functionality, although perhaps it is failing of imagination on my part.
Aha!
I would approach this differently then. There are several ruby libraries for doing design by contract already, including ruby-contract, rdbc, etc.
Another option is to write something like:
def positive
lambda { |x| x >= 0 }
end
def any
lambda { |x| true }
end
class Module
def define_checked_method(name, *checkers, &body)
define_method(name) do |*args|
unless checkers.zip(args).all? { |check, arg| check[arg] }
raise "bad argument"
end
body.call(*args)
end
end
end
class A
define_checked_method(:add, positive, any) do |x, y|
x + y
end
end
a = A.new
p a.add(3, 2)
p a.add(3, -1)
p a.add(-4, 2)
Outputs
5
2
checked_rb.rb:13:in `add': bad argument (RuntimeError)
from checked_rb.rb:29
Of course this can be made much more sophisticated, and indeed that's some of what the libraries I mentioned provided, but perhaps this is a way to get you where you want to go without necessarily taking the path you planned to use to get there?
if you want the value for the default values, too, there's the "arguments" gem
$ gem install rdp-arguments
$ irb
>> require 'arguments'
>> require 'test.rb' # class A is defined here
>> Arguments.names(A, :go)
In fact, the method you describe clearly fails to distinguish arguments from local variables while also failing to work automatically
That's because what you're trying to do is not something which is supported. It's possible (everything is possible in ruby), but there's no documented or known way to do it.
Either you can eval the backtrace like what logan suggested, or you can bust out your C compiler and hack sourcecode for ruby. I'm reasonably confident there aren't any other ways to do this.