I'm trying to write a utility library which tries to call a method on an arbitrary object type. In ruby, I'd do something like:
def foo(object)
object.public_send(:bar)
rescue NoMethodError
raise "Method not defined on object"
end
foo(instance_of_my_arbitrary_class)
I'm not sure how to do this in Crystal, as the type us unknown so I get a compiler error of Can't infer the type of instance variable 'object'.
How do I accomplish this without knowing the type of the object which will be passed?
I think I figured this out after by utilizing a module and including it.
module ArbitraryObject; end
class Arbitrary
include ArbitraryObject
end
class MyLib
def foo(object : ArbitraryObject)
... Code here ...
end
end
MyLib.new(Arbitrary.new).foo
In Crystal, you cannot call arbitrary method on arbitrary objects, as methods are resolved at compile time, not runtime. If the user tries to use your library method with an incompatible type he will get a compile-time error:
def foo(object)
object.bar
end
class MyObj
def bar
puts "bar!"
end
end
foo(MyObj.new) # => "bar!"
Here it works, as an instance of MyObj has the method bar. But if you use something that don't have that method, the user will get a compile-time error:
foo(3) # compile error: undefined method 'bar' for Int32
This error will be showned before the program execution.
Related
In Crystal, is it possible to view metadata on a type's method at compile time? For example, to determine the number of arguments the method accepts, what the type restrictions on the arguments are, etc.
Looking through the API, the compiler's Def and Arg macros have methods which supposedly return this meta information, but I can't see a way of accessing them. I suspect the meta information is only accessible by the compiler.
I found out how to do it. I was looking in the wrong place of the API. The Crystal::Macros::TypeNode has a methods macro which returns an array of method Def (which is how you can access them). It looks like the TypeNode class is the entry point to a lot of good macros.
Usage example
class Person
def name(a)
"John"
end
def test
{{#type.methods.map(&.name).join(', ')}}
end
end
Or
{{#type.methods.first.args.first.name}}
Simply returning the argument name posses an interesting problem, because, after the macro interpreter pastes it into the program, the compiler interprets the name as a variable (which makes sense).
But the real value happens in being able to see the type restrictions of the method arguments
class Public < Person
def name(a : String)
a
end
def max
{{#type.methods.first.args.first.restriction}}
end
end
Person.new.max # => String
I suspect the meta information is only accessible by the compiler.
Exactly. Crystal does not have runtime reflection. You can do a lot with macros at compile time, but once it is compiled, type and method information is no longer available.
But, since everything in a program is known a compile time, you shouldn't really need runtime reflection.
so I am trying to create a crash logging system that would allow a user to add a message through a function addMessage(const char* message) and if he chooses he can allow the crash logger to handle any exception that is thrown in the program by calling a allowGlobalErrorCatching() function.
The thing is that both of these functions are inside a class and what I have done inside allowGlobalErrorCatching() function is I added std::set_terminate(), but when I try to pass a function from a class the compiler gives me an error of
E0167 Argument of type "void(ProcessLogger::*)()" is incompatible with
parameter of type "terminate_handler"
So is there any way to make it accept a class function or do I have to make a global one?
I have the following code:
class Triangle
def initialize(#sides : Array(Int32))
#set = Set.new(#sides)
end
end
However I get a compiler error that gives me:
Can't use Set(T) as the type of instance variable #set of Triangle, use a more specific type
I would have thought since #sides is of type Array(Int32) that there would be type inference on the set. I've read through the docs but not seeing an answer in there.
Essentially, type inference for instance variables is not smart enough to figure out that generic type. I guess it should be possible and will probably be implemented some time, but for now you'll have to write it explicitly.
You have to set the set's type, like this:
class Triangle
def initialize(#sides : Array(Int32))
#set = Set(Int32).new(#sides)
end
end
For instance, I have a definition of type A in moduleA:
Module moduleA
implicit none
type A
private
integer::N
contains
...
procedure,pass::f=>f1
endtype
private::f1
contains
real function f1(self,x)
real::x(self%n)
end function
...
end Module
Then I found I meet a trouble when I override function f in other modules since I can not get the value of N. Even though I can have a method to get that, it seems that something like real :: x(self%get()) is impossible in the declaration. I want to keep the rule of "private" in OPP. So what choices can I have?
You cannot do that. At least in the way you describe. It would be better to see an example of the overriding to suggest other options.
The private attribute works differently in Fortran and therefore you should not directly use rules from other languages that use OOP where private means something different.
In Fortran private is about the local module, in C++ it is about the derived classes. This is very different and you cannot use the C++ (or Java, etc.) rules for private in Fortran when private in fact means something else.
To be clear, I assume you wanted this kind of overriding:
real function f2(self,x)
class(B) :: self
real::x(self%get())
end function
You than get these messages:
private.f90(15): error #8497: Illegal use of a procedure name in an expression, possibly a function call missing parenthesis. [GET]
real::x(self%get())
---------------------^
or
real::x(self%get())
1
Error: 'get' at (1) should be a FUNCTION
So the compilers will not accept a type-bound procedure in the declaration. They would accept an ordinary procedure.
This compiles in gfortran, but not in Intel Fortran. But get() must be public, which you might not want:
real function f1(self,x)
class(A) :: self
real::x(get(self))
end function
real function f2(self,x)
class(B) :: self
real::x(get(self))
end function
In ifort I get
error #8383: The dummy arguments of an overriding and overridden binding that correspond by position must have the same characteristics, except for the type of the passed object dummy arguments.
which is weird.
I have the following problem with the scala type system and I have currently no idea how to fix this.
Basically there is the follow situation:
I have a class, lets call it Actor. This class is templated.
class Actor[T](){
def setValue(value: T): Int = {
//do something with value
}
}
Another class has a method which iterates over a HashMap of the following type:
var newValues = new HashMap[String, Any]()
This HashMap will contain values of type Int and String. The Key of the HashMap identifies a concrete Actor class and ensures that the type of the value fits the templated Actor class it refers to.
A method of the other class iterates over this HashMap:
newValues.foreach(
kv => {
db.getActor(kv._1).setValue(kv._2) //db.getActor returns an Actor identified by kv._1
}
)
Because the concrete value (kv._2) has the same datatype like the templated class has recieved during runtime, I thought the scala engine would be able to cast the any-type into its concrete subtype T.
But I get the following error during compilation:
found : kv._2.type (with underlying type Any)
required: _$3 where type _$3
db.getActor(kv._1).setValue(kv._2)
Does anybody know to fix this problem? I thought by using the super-type Any it would be possible to get around a switch-case and using asInstanceOf[T] of object Any.
Hope somebody can help me!
The problem here is:
Because the concrete value (kv._2) has
the same datatype like the templated
class has recieved during runtime,
Says who?
The compiler couldn't prove, at compile time, that this is, indeed, true. And, basically, it doesn't trust you.
You can always use asIntanceOf to tell the compiler that you know better -- also known as aiming a gun at your foot. And I wonder what type db.getActor returns! I'm half-guessing existential.