macro with typed arguments exception - crystal-lang

I can use typed arguments in methods:
def my_method(val1, val2 : String? = nil)
# something
end
but I can't do same in macro (crystal play example)
macro my_macro(val1, v22 : String?)
puts {{val1}}
puts {{v22}}
end
I've got exception
Syntax error in eval:1: unexpected token: : (expected ',' or ')')
Is it possible to use predefined type in macro?
actualy I need something like:
macro my_macro(val1, val2_key val2 : String? = nil)
# do
end
Thx!

First, the types used in macros are not the same as the types in normal codes, for example, when you do some_macro "a string", at compile time the type of "a string" is StringLiteral.
Second it's currently not possible to have restriction on macro's arguments..
The only thing you can do is remove the type restriction, and add an if statement at the beginning to check the type of the argument, like:
macro foo(arg = nil)
{% unless arg.is_a? StringLiteral || arg.is_a? NilLiteral %}
{% raise "arg must be a string or nil !" %}
{% end %}
end
You can open an issue on the issue tracker on Github if you want this feature, I don't think there's already one.

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

how can use # in let

Here is sample code
(def showscp
( let [ cf (seesaw.core/frame :title "cframe")]
(do
(seesaw.core/config! cf :content (seesaw.core/button :id :me :text "btn" ))
(.setSize cf 300 300)
(seesaw.core/show! cf)
cf
)
)
)
For get button, I use this
(defn find-me
([frame]
(let [ btn (seesaw.core/select frame [:#me] ) ] (do btn)
)
)
)
It cause error, like
Syntax error reading source at (REPL:2:1).
EOF while reading, starting at line 2
(I guess :#me is problem in macro.)
why error cause, and how can I avoid error.
Is there more smart way than (keyword "#me")
# is only special at the beginning of a token, to control how that token is parsed. It's perfectly valid as part of a variable name, or a keyword. Your code breaks if I paste it into a repl, but works if I retype it by hand. This strongly suggests to me that you've accidentally included some non-printing character, or other weird variant character, into your function.
You can't use #, because it is a dispatch character.
# is a special character that tells the Clojure reader (the component that takes Clojure source and "reads" it as Clojure data) how to interpret the next character
The pound character (aka octothorpe) is a special reader control character in Clojure, so you can't use it in a literal keyword, variable name, etc.
Your suggestion of (keyword "#me") will work, although it would probably be better to modify your code to just use the string "#me", or to eliminate the need for the pound-char altogether.

F# if statement function with parameter syntax

The issue is: I cannot figure out what the error is refering to when it diplays
Here is the error:
source_file.fs(10,5): error FS0010: Unexpected keyword 'if' in binding. Expected '=' or other token.
And I've been researching this error and proper syntax for a good while.
Now what I want to do, I hope, is obvious from the general look of the program.
Knowing the correct syntax would be great as microsofts docs are not great.
Seeing as this is case, I just don't understand what could be wrong.
open System
let one = "one"
let two = "two"
if oneortwo one then printfn one + " 1"
else printfn two + " 2"
let oneortwo(a : string)
if a = "one" then return true
elif a = "two" then return false
return false
F# is an expression based language, which means that everything has a value (returns something). F# is also statically typed, so everything returned is of a specific type.
Since everything is an expression, the return keyword is not used. The final expression in a function body is the returned value.
This goes also for if ... then ... else: every branch must return a value and be of the same type.
The correct syntax for your function is
let oneortwo a =
if a = "one" then true
else false
An excellent source of learning F# is Scott Wlaschin's site F# for fun and profit

Declaring union types with nil in Crystal

I've been following the official documentation of Crystal but I couldn't find any details on this. The regular syntax when declaring a union type is String | Int32. However, I've noticed a difference regarding the Nil type.
The regular way of declaring the union still works:
def foo(id : String | Nil)
end
# Overloads are:
# - foo(id : String | Nil)
However I've also seen a shortened syntax which I couldn't find any documentation for:
def foo(id : String?)
end
# Overloads are:
# - foo(id : String | ::Nil)
The result is almost exactly the same except Nil if prefixed with 2 colons. My guess that this is something related to the global scope of Nil as I've seen a similar syntax in other languages.
Is String | Nil and String? the same thing, and when should you use one vs the other?
What's the meaning of 2 colons in the type signature (e.g. ::Nil)?
yes, they're exactly the same, people typically use the Foo? version as it's shorter.
::Nil means "the class Nil at the root namespace". This means that if you define a different Nil class in a different namespace, ::Nil always refers to the nil in the stdlib.

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.