Crystal check if subclass of list of classes - crystal-lang

So what I want to do is check if a raised error is a subclass of a list of specific Exceptions at runtime. Users can hand in an Array of Exceptions at runtime.
I thought I'd just use is_a? and it works as expected against a single class.
class A < Exception; end
class B < A; end
class C < Exception; end
class D < Exception; end
begin
raise B.new
rescue e
puts e.is_a? A
end
But if I then use an array of Exceptions to check, it doesn't work anymore
monitored = [A, C]
begin
raise B.new
rescue e
monitored.each do |exception_class|
puts e.is_a? exception_class
end
end
The error I get is Syntax error in eval:24: expecting token 'CONST', not 'exception_class' with line 24 being puts e.is_a? exception_class.
puts typeof(exception_class) in the loop prints Exception:Class as expected.
Any ideas what I am doing wrong here?

so what I want to do is check if a raised error is a subclass of a list of specific Exceptions.
You can rescue by type(s):
begin
raise B.new
rescue e : A | C
puts "A or C"
rescue B
puts "B"
rescue
puts "any other kind of exception"
end
which prints
# => A or C

Apparently this is not possible for the exact case mentioned in the question with the current compiler implementation:
https://github.com/crystal-lang/crystal/issues/2060#issuecomment-309711343

Related

How do I make generic memoization in Crystal?

I want to define a generic memoizing wrapper in Crystal.
I have the following crystal code:
module Scalar(T)
abstract def value: T
end
class ScSticky(T)
include Scalar(T)
def initialize(sc : Scalar(T))
#sc = sc
#val = uninitialized T
end
def value: T
#val ||= #sc.value
end
end
In other words I want ScSticky to call the underlying Scalar(T) only once and return the cached output for all subsequent calls.
However, the above approach doesn't work if T is Int32
For instance, when wrapping this class
class ScCounter
include Scalar(Int32)
def initialize
#val = 100
end
def value: Int32
#val += 1
#val
end
end
ScSticky(ScCounter.new).value will always be equal to 0 (as I understand it, because the unitialized Int32 is actually initialized with 0 value)
I would highly appreciate some help with this problem
Upd: It seems that the proper way to implement this is using nil , however I have problems with understanding how exactly such implementation should look. I also want to be able to memoize .value method even if it returns nil (In other words, if the T is a nilable type)
You are using an unsafe feature "uninitialized", which means, "keep whatever was there in memory previously" (in theory the value is random and possibly invalid, in practice you often end up with 0 anyway -- but it's still not guaranteed at all).
The short story about the uninitialized feature is please never use it.
This behavior wouldn't surprise you if you wrote #val = 0 -- and that's kinda what you wrote.
You must define #val : T? = nil -- to make it nilable (have this separate possible value of nil, which is its own type - Nil).
You may have thought that unitialized brings nil into the picture, but it definitely doesn't.
In response to your comment about also including nil into possible values, here's a full solution, which, instead of Nil, uses a unique "sentinel" struct, that the user can never create.
module Scalar(T)
abstract def value: T
end
private struct Sentinel
end
class ScSticky(T)
include Scalar(T)
#val : T | Sentinel = Sentinel.new
def initialize(#sc : Scalar(T))
end
def value: T
val = #val
if val.is_a?(Sentinel)
#val = #sc.value
else
val
end
end
end
class ScCounter
include Scalar(Int32)
def initialize
#val = 100
end
def value: Int32
#val += 1
end
end
sc = ScSticky.new(ScCounter.new)
p! sc.value #=> 101
p! sc.value #=> 101

Eiffel: classical typecasting switch structure with attached and inspect

What is the best practice to do something such as
local
l_pet: ANIMAL
do
l_pet := catch_it_from_the_sky
inspect l_pet
when attached {DOG} l_pet as l_dog
l_dog.eat (meat)
when attached {FISH} l_pet as l_fish
l_fish.eat (plants)
else
io.put_string ("Strange animal how do I feed him???")
end
do
the compiler is complaining with the attached after when...
Update: why such a need?
because it just happened me to mess up with repeated copy-paste which is what a language tries to help avoiding. In the above case, the l_pet is written one time, with a N times if/else I'd have to write it as much times as ifs...
An inspect statement allows for checking if an expression has a specific value, and can be applied to expressions of integral types (such as INTEGER_64, CHARACTER_32 or NATURAL_8):
inspect age
when 6 .. 16 then ...
when 18 then ...
when 80, 90 then ...
...
end
For discriminating over object types, conditional instructions are used:
if attached {DOG} pet as dog then
dog.eat (meat)
elseif attached {FISH} pet as fish then
fish.eat (plants)
else
io.put_string ("Strange animal how do I feed him???")
end
In a multi-branch instruction
inspect exp when ... then ... else ... end
The exp expression needs to be a character or an integer expression.
In your given example I don't see the need to do that Object-Test,
but if you need to do something like that you need to use the conditional instruction.
if ... then
...
elseif ... then
...
else
...
end

What is the "select when" syntax for?

Experimenting with the language I've found that select is defined in the global scope and its precedence is higher than local variables.
def example(select)
puts select
end
example 3
# Syntax error in eval:3: unexpected token: end (expecting when, else or end)
So experimenting with select step by step I get this:
select 1 end
# Syntax error in eval:3: unexpected token: end (expecting when, else or end)
and then
select when 1 end
# Syntax error in eval:1: invalid select when expression: must be an assignment or call
then
select when x = 1 end
# Syntax error in eval:1: invalid select when expression: must be an assignment or call
then
select when x
# Syntax error in eval:1: unexpected token: EOF (expecting ',', ';' or '
I'll skip ahead a few steps as you should have an idea of how I've come to my question…
select when x;
else y
end
# Error in line 1: undefined local variable or method 'x_select_action'
and lastly
x_select_action = 4
select when x;
else y
end
# Error in line 3: undefined method 'x_select_action' (If you declared 'x_select_action' in a suffix if, declare it in a regular if for this to work. If the variable was declared in a macro it's not visible outside it)
So there is this keyword in the language which precedes local variables precedence and I don't know what it's for. But apparently it looks for x_select_action when x is given as a when clause. What is this select for and how is it meant to be used?
Searching online I see select defined on Enumerable, Hash, Channel, and Array… but at first glance these don't seem to be it.
Thanks for the help!
It's similar to Go's select: https://tour.golang.org/concurrency/5
But it still needs some tweaks to be finished, that's why there are no docs about it yet.

How can I avoid over-testing with example

I've been trying to start testing my code more, and I thought I would mimic the style of some of the tests that were autogenerated for by me rails scaffolding (I'm using rspec and rails, but my question is really just general-tdd). However, those methods were very basic, along the lines of do something. then if foo, do something else. I feel like as soon as you get more complex by adding one more 2 more things to condition in your if, things start spiraling and you get nested examples all over the place. Here's an example of what i've been doing that feels like overkill.
First the method
def maybe_blah(foo)
if foo != nil && foos.find_by_id(foo.id) != nil
blah unless bar?(foo)
end
end
this method is pretty simple, here's how I was planning on testing it
describe maybe_blah
shared_examples "what happens when bad input is passed" do
it "doesn't call bar?"...
it "doesn't call blah"...
end
context "when foo is not nil"
context "when foo is in foos"
context "bar? returns true"
it "doesn't call blah" do
some code involving mocking bar? and blah
end
end
context "bar? returns false"
it "does call blah" do
some code involving mocking bar? and blah
end
end
end
context "when foo is not in foos"
include_examples "what happens when bad input is passed"
end
end
context "when foo is nil"
include_examples "what happens when bad input is passed"
end
end
That's noticeably shorter than what the test would be if there was all of the setup and whatever else in there (testing maybe_blah for real like that took me 55 lines), so you can see how it seems to get out of hand. Is there a good way of testing a method that does feel like such overkill.
I don't see a way around having 3-deep nesting when you have 3 conditions you're testing (at least without repeating yourself even more), but it seems like you'd need to do that to make sure you're handling all different cases. Also, it seems dumb to test the fail result for every different bad input, but how else would you know you're actually failing on those bad inputs?
So is this just overkill?
Actually you condition is the same as:
def maybe_blah(foo)
if foo != nil && foos.find_by_id(foo.id) != nil && !bar?(foo)
blah
end
end
Thus you can extract it into separate method using Decompose Conditional and Consolidate Conditional Expression techniques:
def maybe_blah(foo)
blah if can_do_it?(foo)
end
def can_do_it?(foo)
foo != nil && foos.find_by_id(foo.id) != nil && !bar?(foo)
end
After that you can test this method in two contexts
describe '#maybe_blah' do
context 'when can do' do
# stub can_do_it? and returns true
# stould receive :blah
end
context 'when cant do' do
# stub can_do_it? and returns false
# should not receive :blah
end
end
And test condition separately.
And you can omit != nil
def can_do_it?(foo)
foo && foos.find_by_id(foo.id) && !bar?(foo)
end

Getting error description from compiling regexp

I am trying to compile a regexp and get an error message that can be presented to the user. I tried this with Text.Regex.TDFA and Text.Regex.Posix and it seems to behave the same:
Prelude Text.Regex.TDFA Data.Maybe Data.Either.Utils> fromLeft $ (makeRegexM ".[" :: Either String Regex)
"*** Exception: parseRegex for Text.Regex.TDFA.String failed:".[" (line 1, column 3):
unexpected end of input
expecting "^", "]", "-" or Failed to parse bracketed string
Prelude Text.Regex.TDFA Data.Maybe Data.Either.Utils> isJust $ (makeRegexM ".[" :: Maybe Regex)
False
Prelude Text.Regex.TDFA Data.Maybe Data.Either.Utils> isJust $ (makeRegexM "." :: Maybe Regex)
True
The Maybe monad seems to work; the Either does not. However the documentation says, it should use 'fail' - which, as far as I know, is defined in Either monad. Am I doing something wrong?
The reason probably is that the monad instance for Either e recently changed. In mtl-1.*, there used to be an
instance Error e => Monad (Either e) where
...
fail msg = Left (strMsg msg) -- I may misremember the exact names
so calling fail there didn't cause an exception. Now, there is a monad instance in base (Control.Monad.Instances)
instance Monad (Either e) where
...
fail s = error s -- implicitly from the default method for fail
so you get the above.