Adding a nilable variable to a non-nilable array - crystal-lang

I have a string array named current_todos and am trying to add a variable of type (String | Nil) named new_task by doing the following:
current_todos << new_task if typeof(new_task) == String
I get the error Error: no overload matches 'Array(String)#<<' with type (String | Nil).
How can I add a nilable string to current_todos after doing a type check?
Edit: here is the full code:
require "option_parser"
current_todos = [
"Laundry",
"Walk dog",
"Sing in shower"
]
new_task = nil
OptionParser.parse do |parser|
parser.banner = "Welcome to my todo app!"
parser.on "-a TASK", "--add TASK", "Type a new task" do |task|
new_task = task
end
parser.on "-h", "--help" do
puts parser
exit
end
end
current_todos << new_task if typeof(new_task) == String
current_todos.each do |todo|
puts todo
end

If new_task is of type String|Nil, you can test if it is non-nil. Then the compiler will know that it is a string. That here should work:
current_todos << new_task if new_task
Another way that the compiler will understand, which is closer to your original code, is to use is_a?:
current_todos << new_task if new_task.is_a?(String)

typeof(var) gives the compile time type, not the type at runtime. For example:
def check_type(var : String?)
puts typeof(var)
puts var.class
puts var.is_a? String
puts var.is_a? Nil
puts var.nil?
end
check_type(gets)
If you give input, it will print:
(String | Nil)
String
true
false
false
If you don't give input (gets returns nil) then it will print:
(String | Nil)
Nil
false
true
true

Looks like the reason something like current_todos << new_task if new_task.is_a?(String) doesn't work is because the new assignment of new_task happens within the .on function of the parser. Since the compiler doesn't know when/if this is called as it is inside a closure, it resorts to nil.
To force it to work I would need to use .not_nil!

Related

Crystal-Lang - Cross-Macro Macro-variables

So I'm building a data type where, I would like, optional auto-casting. The last question I asked is related to this also.
The code I currently have can be found below:
class Test(T)
##auto_cast = false
def initialize(var : T)
#var = var
end
def self.auto_cast
##auto_cast
end
def self.auto_cast=(val)
##auto_cast = val
end
def self.auto_cast(forced_value=true,&block)
#Force value, but store initial value:
ac = ##auto_cast
##auto_cast = forced_value
block.call
##auto_cast = ac
end
def +(val)
var = #var
if ##auto_cast
if var.is_a? String
casted_arg = val.to_s
return var + casted_arg
else
casted_arg = typeof(var).new(val)
return var + casted_arg
end
else
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
return var + val
end
end
end
end
When I try to test the data type however:
Test.auto_cast do
puts Test.auto_cast
puts Test.new(1) + "1"
puts Test.new("1") + 1
end
It throws an error at return var + val:
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
ERROR! --> return var + val
end
At first I was confused why, but now it makes sense.
I believe the Crystal compiler cannot be certain that ##auto_cast will be true whenever I intend to auto_cast (and to be fair, when auto-casting is disabled, I want the syntax error).
A compile error occurs because the value of ##auto_cast is unknown at compile time.
Due to the contradictory nature of the bodies:
.
if var.is_a? String
casted_arg = val.to_s
return var + casted_arg
else
casted_arg = typeof(var).new(val)
return var + casted_arg
end
and
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
return var + val
end
Each definition should only be used when the user explicitly declares it. Thus this is more suited to a macro.
Given these reasons I started trying to build the functionality into a macro instead:
def +(val)
var = #var
{%if ##auto_cast%}
if var.is_a? String
casted_arg = val.to_s
return var + casted_arg
else
casted_arg = typeof(var).new(val)
return var + casted_arg
end
{%else%}
if typeof(var) != typeof(val)
{{raise "Error: Type of <<var>> is not equal to type of <<val>> while auto_cast is false."}}
else
return var + val
end
{%end%}
end
I thought this would work because, this way code is only ever generated if ##auto_cast is set. However what I forgot was premise #2. I.E. the value of ##auto_cast is unknown at compile time. Ultimately, in order to make this work I would need a variable which can be:
Set at compile time.
Used globally within macros at compile time.
Ultimately I figured I could do something along the lines of this:
SET_AUTOCAST_VARIABLE true
puts Test.auto_cast
puts Test.new(1) + "1"
puts Test.new("1") + 1
SET_AUTOCAST_VARIABLE false
and then in the +() definition:
{%if autocast_variable %}
...
{%else%}
...
{%end%}
The problem is, I do not think such a macro global variable exists... I was thinking about ways to get around this issue and so far the only solution I can come up with is using some external file at compile time:
{{File.write("/tmp/cct","1")}}
puts Test.auto_cast
puts Test.new(1) + "1"
puts Test.new("1") + 1
{{File.write("/tmp/cct","")}}
and in the method definition:
{%if File.read("/tmp/cct")=="1" %}
...
{%else%}
...
{%end%}
This feels really hacky though... I was wondering whether there were any other alternatives, (or, even, if this just won't work at all)?
This can't work. Methods are only instantiated once, it is not possible to have two implementations of the same method with the same argument types. In the following example both + methods will inevitably have the same implementation.
Test.auto_cast do
Test.new(1) + "1"
end
Test.new(1) + "1"
You can't have different implementations of the same method depending on lexical scope. The method is exactly instantiated once and so is the macro inside it.
I don't understand your overall use case, but maybe there are other ways to achieve what you need.
For completeness: You can utilize a constant as a macro global variable. Constants can't be redefined, but altered through macro expressions. That can be used to store state between macros. For example:
FOO = [true]
{{ FOO[0] }} # => true
{% FOO.clear; FOO << false %}
{{ FOO[0] }} # => false
That's pretty hacky, though ;)

F# test if the result is Some(any)

I need to check whether my validation function returns something when fails or none. If it returns Some<string>, there is a validation error, otherwise it's valid and the function returns None.
This is my attempt , but it's not refactoring safe:
[<Property(Arbitrary=[| typeof<Strings.WithLenFrom1To100> |])>]
let ``lengthValidator should return some error when the string's length is longer than expected``(str:string)=
let maxValidLen = str.Length-1
let actual = lengthValidator maxValidLen str
let expected = Some(sprintf "Maximum string length is %i but %s contains %i characters" maxValidLen str str.Length)
//How can I say **let expected = Some(anything) **instead
test <#actual = expected#>
test <# actual.IsSome #>

Error while passing strings in functions in Haskell

So I tried the following code in haskell where I try to detect if the user has entered a "no" or "No" in the string. Also I tried replacing [[Char]] with Strings but it gives compilation errors.
wantGifts :: [[Char]] -> [[Char]]
wantGifts st = [if (x == "No" || x== "no") then "No gifts given" else "But why" | x <- st, x == head st]
The above code compiles but when I pass a string to it, it returns an error message:
*Main> wantGifts "no I dont"
<interactive>:8:11:
Couldn't match type ‘Char’ with ‘[Char]’
Expected type: [[Char]]
Actual type: [Char]
In the first argument of ‘wantGifts’, namely ‘"no I dont"’
In the expression: wantGifts "no I dont"
In an equation for ‘it’: it = wantGifts "no I dont"
Look closely at the type of wantGifts, it requires a List of List of Chars. But "no I dont" is of type String which is just [Char]. With your current construction, you have to use:
wantGifts ["no I dont"]
There are several ways to improve this, best is to use Text.
import Data.Text (Text)
import qualified Data.Text as T
wantGifts :: Text -> Text
wantGifts txt = if (T.isInfixOf "no" . T.toLower) txt then "No gifts given" else "But why"
You have defined wantGifts as taking a list of strings. [[Char]] is equivalent to [String]. In the REPL, you are passing it a single string.
If you instead did this, it would compile:
wantGifts ["no I dont"]
However, I have a hunch this isn't what you want.
If you were trying to detect whether the word "no" was anywhere in the string, you could use the words function:
containsNo :: String -> Bool
containsNo = any (\w -> w == "no" || w == "No") . words

Rails4: Use .include? and LIKE in an array query

I have an array:
#available_languages = ["English", "German", "Japanese"]
and a variable:
#needed_language = "Basic English"
I want to show 'Available!' in the view if a #needed_language is present in the #available_languages array, even if the names don't match exactly, such as 'English' and 'Basic English'
Right now, my conditional only returns true for exact matches. How do I return true for non-exact matches?
- if #available_languages.map{|a| a.available_language_name}.include? #needed_language.needed_language_name
Available!
- else
Not Available
You could use select function, something like this:
#available_languages = ["English", "German", "Japanese"]
#needed_language = "Basic English"
if #needed_language.split(" ").select{ |a| a if #available_languages.include? a} then
"Available"
else
"Not Available"
endif

How to create Procs with variable arguments in Crystal?

I want to pass arguments to a Proc like using splat, but it returns an error wrong number of block arguments (1 for 0)
$callbacks = {} of String => Array(->)
def add_callback(event, &block)
begin
$callbacks[event.to_s] << block
rescue
$callbacks[event.to_s] = [block]
end
end
add_callback(:event) do |arg|
puts "event!"
end
$callbacks["event"].first.call
Error in line 11: wrong number of block arguments (1 for 0) http://carc.in/#/r/7gw
You need to specify the argument list everywhere.
class EventManager
def initialize
#callbacks = Hash(String, Array(String ->)).new {|h, k| h[k] = [] of String -> }
end
def add(event, &callback : String ->)
#callbacks[event] << callback
end
def fire(event, argument : String)
#callbacks[event].each &.call(argument)
end
end
callbacks = EventManager.new
callbacks.add("foo") do |argument|
puts "Got #{argument}"
end
callbacks.add("bar") do
puts "I was called"
end
callbacks.fire "foo", "Ping"
callbacks.fire "bar", "Pong"