This compiles but doesn’t do anything useful, it just puts one of the argument types in both A and B, and I am wondering if I should file it as a bug or not.
def contains(type : A|B) forall A, B
puts "Called for union."
end
This compiles but doesn’t do anything useful, it just puts one of the argument types in both A and B, and I am wondering if I should file it as a bug or not.
def contains(type : A|B) forall A, B
puts "Called for union."
end
The thing here is that the parameter type restrictions are used to solve the method dispatch, but the invocation happens with actual types.
With the current state is like if the parameter types restrictions are removed completely and a method specialization appears instead for the call site.
What is the call to that method and what would you expect to happen?
Also note that mathematically, and in the language, T | T is just T. So A and B could be the same type just fine.
This was intended to be a method to emit the type of the contents of a Generic.
I have implementations like this:
def contains(type : Array(T)) forall T
T
end
def contains(type : Hash(K, V)) forall K, V
V
end
The problem is that sometimes there are union types such as Array(X) | Nil. So, I can have an implementation like this:
def contains(type : A.class|B.class) forall A, B
end
and that will be called, but just returns one type in the union, the same one for both A and B.
I guess I have no choice but to use macros and pick apart TypeNodes?
Thanks
Bruce
Would something like this do the trick for you?
def remove(a, t : T.class) forall T
raise "" if a.is_a?(T)
a
end
v = 3 || "str"
pp!({v, v.class, typeof(v)}) # => {3, Int32, (Int32 | String)}
w = remove(v, String)
pp!({w, w.class, typeof(w)}) # => {3, Int32, Int32}
not_nil! and compact_map does something similar to remove Nil
from the result. This is a more generic instance of that.
I would not assume unions have an order.
Well, that is the most interesting expression I’ve seen in the language so far. It looks like it’s forcing the else
condition of a is_a?(T)
, but I don’t understand how. Is this a guaranteed feature of the language?
If a == "str"
the remove(a)
would have raise on runtime, is not different from {Object, Nil}#not_nil!
.
Since raise "" : NoReturn
the returning type is typeof(a) - T | NoReturn
which is typeof(a) - T
.
The program is equivalent to:
def remove(a, t : T.class) forall T
if a.is_a?(T)
raise ""
else
a
end
end
Maybe that has a more straightforward interpretation.
What is interesting is that S - T
can’t be expressed in the type grammar, but yet this things can be done.
Enumerable(T)#compact_map
is able to return Array(T - Nil)
due to typeof
+ not_nil!
def compact_map
ary = [] of typeof((yield first).not_nil!)
each do |e|
# ...
end
ary
end
Thank you. I think that because this:
def one_type_of(type, A|B|C|D|E|F|G|H), forall A,B,C,D,E,F,G,H
A
end
Returns exactly one of the types in the union type, I think I can use the remove() method you wrote to enumerate all of the types. It would be recursive because of the need to define a new variable in each pass.
To get one of the types (in a deterministic form) going to macros will be easier, definitely
def one_type_of(t : T.class) forall T
{{T.union_types.first}}
end
pp! one_type_of(Union(String, Int32)) # => Int32
pp! one_type_of(Union(Bool, Nil)) # => Bool
pp! one_type_of(Char) # => Char
I’m still not sure the experiment you want, it seems you aim for these operations to play with types
def remove(s : S.class, t : T.class) forall S, T
x = uninitialized S
raise "" if x.is_a?(T)
typeof(x)
end
def one_type_of(t : T.class) forall T
{{T.union_types.first}}
end
def atomic(t : T.class) forall T
{{T.union_types.size}} == 1
end
pp! one_type_of(String | Int32) # => Int32
pp! remove(String | Int32, one_type_of(String | Int32)) # => String
t = String | Int32 | Bool
pp! t, atomic(t), one_type_of(t)
# => (Bool | Int32 | String)
# => false
# => Bool
t = remove(t, one_type_of(t))
pp! t, atomic(t), one_type_of(t)
# => (Int32 | String)
# => false
# => Int32
t = remove(t, one_type_of(t))
pp! t, atomic(t), one_type_of(t)
# => String
# => true
# => String
Well, it seems just using macros is easier. I didn’t understand to use them in a generic, as you have. I really appreciated learning that bit of language voodoo, as well. I’ll keep it in my tool bag.
Thanks
Bruce
Could you maybe show some example calls and expected outputs, so we can understand what exactly you are trying to achieve?
May I scream for just a tiny moment?
Bruce Perens? The Bruce Perens?