Ruby has this method called the block_given in it so that we can check to see if a block is passed and process accordingly if given. Is there an equivalent method in crystal?
Crystal does not have it for a moment. But you can have similar behavior using method overloading:
def foo
foo {}
end
def foo
yield
end
foo { }
foo
If you absolutely must know whether a block was given, you can do something like this:
def foo(block : (String ->)? = nil)
if block
block.call("Block given")
else
puts "No block given"
end
end
def foo(&block : String ->)
foo(block)
end
foo { |s| puts s }
foo
(For more information on Proc syntax, see https://crystal-lang.org/reference/syntax_and_semantics/type_grammar.html#proc)
Related
Let's say I have a class
class MyClass
def sayMyName()
puts "I am unknown"
end
end
and I have stored this method name in a variable: methodName = "saymyName"
I want to call this method by using above variable, something like this:
instance = MyClass.new
instance[methodName]
I know it can be called using a macro but I don't get how? Please someone provide an example with explanation.
Update 1
There is already an answer for this: Calling methods dynamically (Crystal-lang)
but this doesn't answer how to do it when methods are inside a class.
I have adapted the example given in the update:
class Foo
def method1
puts "i'm method1"
end
def method2
puts "i'm method2"
end
def method3
puts "i'm method3"
end
def bar
{ "ctrl": -> { method1 },
"shift": -> { method2 },
"alt": -> { method3 }
}
end
def [](method)
bar[method]
end
end
binding = ["ctrl", "shift", "alt"].sample
foo = Foo.new
foo[binding].call #=> one of them
Working Example
class Foo
def initialize(#foo : String | Nil)
end
def foo
#foo
end
end
a = Foo.new "213"
if !a.foo.nil?
puts a.foo, typeof(a.foo)
end
get output of
213
(String | Nil)
but shouldn't type of a.foo be narrowed to String? Is this another design limit?
The compiler doesn't know that #foo doesn't change. Say your class Foo has a setter for #foo. If some concurrently running code uses that setter to set #foo to nil, the second call to Foo#foo inside the if condition might return nil now even though the check passed before.
You can remedy by introducing a local variable which the compiler can reason about:
if foo = a.foo
puts foo, typeof(foo)
end
I have a groovy script defined, which includes an inner class
foo.groovy:
// does something
Bar {
int foobar() {
return 1
}
}
// Does something else
Why I have the class defined in the script is a longer story, but it's necessary, unless I want to make a big redesign of the project structure (And the customer doesn't want to pay for this time)
Anyway, I have a GroovyTestCase, where I want to call this class from.
I want to do something like this:
class Test extends GroovyTestCase {
void testSomething() {
Bar bar = new Bar()
assertTrue(1, bar.foobar())
}
}
Is it possible to reference a class defined in a groovy script? If so, how?
foo.groovy
println "foooo 01"
class Bar {
int foobar() {
return 1
}
}
println "foooo 02"
Test.groovy (you can make it as a class)
def shell = new GroovyShell()
//parse script
def script = shell.parse(new File("/11/foo.groovy"))
//get class loader used for script parsing
def cl = shell.getClassLoader()
//get list of parsed/loaded classes
println cl.getLoadedClasses() // [class Bar, class foo]
def bar = cl.loadClass('Bar').newInstance()
println bar.foobar() // 1
but beware! if your foo.groovy script loaded some other classes that you want to access in testcase, you have to do it through the same shell classloader, otherwise there could be strange errors like could not assign String to String...
This test is OK, because I use toString explicitly
"have one sentence by '...'" in {
val text1 = Text("some text...")
text1.sentences must have size(1)
text1.sentences(0).toString must_== "some text"
}
If without toSting the test it fails with message like:
Expected :some text
Actual :some text
java.lang.Exception: 'some text: dictionary.Sentence' is not equal to 'some text: java.lang.String'
I understand the sense of it (in general), but since toString is
invoked anyway shouldn't it check string to string then?
What is the best way to write this test to be concise? Without using
toString directly.
I think you might be confusing toString and equals. When you say what you wanted to say:
text1.sentences(0) must_== "some text"
What you are really saying is:
text1.sentences(0).equals("some text") must beTrue
If you want this to work, then you would need there to be an equals function on the Sentence class that used the toString of the sentence to compare to the incoming object (a String in this case). A simple spec showing that could look like this:
class Sentence(text:String){
override def equals(obj:Any) = {
this.toString == obj.toString
}
override def toString = text
}
class EqualitySpec extends Specification{
"A sentence" should{
"be equal to plain text" in {
val sentence = new Sentence("hello world")
sentence must be_==("hello world")
}
}
}
Now this works great if that Sentence class is your own class. If it's in a third party library and you have no control over the equals function then you might be stuck with things the way that they are.
I'm trying to get my head around the covariance of Scala's collections. I have the following:
abstract class MediaFormat{
def name:String
def status:String
}
case class H264_high(status:String="on") extends MediaFormat {
def name = "h264_high"
}
case class H264_med(status:String="on") extends MediaFormat {
def name = "h264_med"
}
case class H264_low(status:String="on") extends MediaFormat {
def name = "h264_low"
}
case class H264_syndication(status:String="off") extends MediaFormat {
def name = "h264_syndication"
}
What I wanted to do was have a set of all of these formats because I need a collection where each format only occurs once, so I tried:
object MediaFormat {
val allFormats:Set[MediaFormat] = Set(H264_high,H264_low)
}
This gave me a compile time exception because, as I now understand, Set is invariant.
So I think, well I guess I'll just have to use a List and manage the repeated values myself
but then I try this:
object MediaFormat {
val allFormats:List[MediaFormat] = List(H264_high,H264_low)
}
because as I understand it, List is covariant, but that still doesn't compile.
Can someone help me understand what I should do to get a collection of my formats?
It doesn't compile because you are referencing the companion object (module), not the case classes! The compile error (which you should have posted) is nothing to do with variance. It will work, with Set if you do this:
val allFormats: Set[MediaFormat] = Set(H264_high(), H264_low())
^^ ^^
Or alternatively;
val allFormats = Set[MediaFormat](H264_high(), H264_low())
However, it makes no sense for these to be case classes given your description of the problem; I would just make them modules, i.e.
case object H264_syndication extends MediaFormat {
def status = "off"
def name = "h264_syndication"
}
Then your original code will work just fine. Or I would make them vals as follows:
case class MediaFormat(status: String, name: String)
val H264_syndication = MediaFormat(status ="off", name = "h264_syndication")
I think this would be my preference; I rarely use abstract classes any more to be honest (normally, I am dishonest).
Explanation: Covariance means the following:
G[S] <: G[T] iff S <: T
The fact that Set is invariant, means that a Set[S] is not a subtype of Set[T] (for S <: T), but it does not mean that such a Set[T] may not contain elements of type S.