Is there a way to specify various types for a parameter - eiffel

Is there a way to restrict the conformance of a type to be a collection of types?
Let me explain by example:
give_foo (garbage: ANY): STRING
do
if attached {STRING} garbage as l_s then
Result := l_s
elseif attached {INTEGER} garbage as l_int then
Result := l_int.out
elseif attached {JSON_OBJECT} garbage as l_int then
Result := l_int.representation
elseif attached {RABBIT} garbage as l_animal then
Result := l_animal.name + l_animal.color
else
Result := ""
check
unchecked_type_that_compiler_should_be_able_to_check_for_me: False
end
end
end
Couldn't I do something like (like a convert function could do)
give_foo (garbage: {STRING, INTEGER, JSON_OBJECT, RABBIT}): STRING
do
if attached {STRING} garbage as l_s then
Result := l_s
elseif attached {INTEGER} garbage as l_int then
Result := l_int.out
elseif attached {JSON_OBJECT} garbage as l_int then
Result := l_int.representation
elseif attached {RABBIT} garbage as l_animal then
Result := l_animal.name + l_animal.color
else
Result := ""
check
unchecked_type_that_compiler_should_be_able_to_check_for_me: False
end
end
end
or something like
not_garbage_hash_table: HASH_TABLE[{INTEGER, STRING, ANIMAL}, STRING]

Conformance to a collection of types is not supported for several reasons:
Calling a feature on an expression of such a type becomes ambiguous because the same name could refer to completely unrelated features.
In one case we need a sum (disjoint union) of types, in the second - plain union, in the third - an intersection, etc. And then, there could be combinations. One would need an algebra built on top of a type system that becomes too complicated.
If the requirement is to check that an argument is one of expected types, the following precondition can be used:
across {ARRAY [TYPE [detachable ANY]]}
<<{detachable STRING}, {INTEGER}, {detachable JSON_OBJECT}>> as t
some argument.generating_type.conforms_to (t.item) end
A common practice to process an expression of a potentially unknown type is a visitor pattern that deals with known cases and falls back to a default for unknown ones.

Possibly place Alexander's solution in a BOOLEAN query so it can be reused?
is_string_integer_or_json_object (v: detachable ANY): BOOLEAN
-- Does `v' conform to {STRING}, {INTEGER}, or {JSON_OBJECT}?
do
across {ARRAY [TYPE [detachable ANY]]}
<<{detachable STRING}, {INTEGER}, {detachable JSON_OBJECT}>> as t
some v.generating_type.conforms_to (t.item) end
end

Related

Eiffel: How do I compare the type of an object to a given type?

How do I compare the type of an object to a given type (instanceOf statement in Java)?
do_stuff (a_type: TYPE)
local
an_object: ANY
do
an_object := get_from_sky
if an_object.instanceOf (a_type) then
io.putstring("Object is same type as parameter")
else
io.putstring("Object is NOT same type as parameter")
end
end
Depending on the generality of the solution, there are different options. First, consider the case when an_object is always attached:
a_type always denotes a reference type.
For reference types one can use the feature attempted (with an alias /) of the class TYPE.
if attached (a_type / an_object) then
-- Conforms.
else
-- Does not conform.
end
a_type can denote either a reference or expanded type.
The feature attempted of the class TYPE is unusable in this case because it would always return an attached object for expanded types. Therefore, the types should be compared directly.
if an_object.generating_type.conforms_to
(({REFLECTOR}.type_of_type ({REFLECTOR}.detachable_type (a_type.type_id))))
then
-- Conforms.
else
-- Does not conform.
end
If an_object could also be Void, the condition should have additional tests for voidness. Denoting the conditions from the cases above with C, the tests that handle detachable an_object would be:
if
-- If an_object is attached.
attached an_object and then C or else
-- If an_object is Void.
not attached an_object and then not a_type.is_attached
then
-- Conforms.
else
-- Does not conform.
end
If you want to check if the type of a_type is identical to the type of an_object,
use the feature {ANY}.same_type, if you want to check type conformance just use {ANY}.conforms_to
do_stuff (a_type: TYPE)
local
an_object: ANY
do
an_object := get_from_sky
if an_object.same_type (a_type) then
io.putstring("Object an_object is same type as argment a_type")
elseif an_object.conforms_to (a_type)
io.putstring("Object an_object conforms to type of a_type ")
else
io.putstring ("...")
end
end

Eiffel: a way to evaluate a STRING expression like `eval`

Is there a way to evaluate a STRING expression in Eiffel? (A big source of error I know... but a powerful mechanism!)
Looking for a way to do a generic setting mechanism for database fields and classes I'm trying to do something like.
fields_mapping: HASH_TABLE [ANY, STRING]
do
create Result.make (10)
Result.put (name, "name")
Result.put (surname, "surname")
...
end
set_fields
do
across
fields_mapping as field
loop
eval("set_" + field.key + "(" + field.item + ")")
end
end
I know I could do it with agents, but it seems for me less generic as I have to define every function 2 times
in the fields_mapping
in another fields_mapping I have for JSON conversion
Existing implementations of Eiffel are compiling rather than interpreting. Therefore, evaluation of arbitrary expressions is not supported. Updating arbitrary object fields using given values is still possible using reflection classes:
update_object (field_values: HASH_TABLE [ANY, STRING]; target: ANY)
-- Update fields of object `target` with values from `field_values`.
local
object: REFLECTED_REFERENCE_OBJECT
fields: like field_properties
position: like {REFLECTED_OBJECT}.field_count
do
create object.make (target)
fields := field_properties (object.dynamic_type)
across
field_values as v
loop
if attached fields [v.key] as f then
position := f.position
if {REFLECTOR}.field_conforms_to (v.item.generating_type.type_id, f.type_id) then
inspect object.field_type (position)
when {REFLECTOR_CONSTANTS}.integer_32_type then
object.set_integer_32_field (position, {INTEGER_32} / v.item)
when ... then
-- Other basic types.
when {REFLECTOR_CONSTANTS}.reference_type then
object.set_reference_field (position, v.item)
else
print ("Unhandled field type%N")
end
else
print ("Field type mismatch%N")
end
end
end
end
The code above is a bit simplified because it does not handle types SPECIAL and TUPLE as well as user-defined expanded types. The code relies on a helper feature that records type information so that next time it should not be recomputed from scratch:
field_properties (type_id: like {TYPE [ANY]}.type_id):
HASH_TABLE [TUPLE [position: INTEGER; type_id: INTEGER], STRING]
-- Positions and types of fields indexed by their name
-- for a specified type ID `type_id`.
local
i: like {REFLECTOR}.field_count_of_type
do
Result := field_positions_table [type_id]
if not attached Result then
from
i := {REFLECTOR}.field_count_of_type (type_id)
create Result.make (i)
field_positions_table.force (Result, type_id)
until
i <= 0
loop
Result [{REFLECTOR}.field_name_of_type (i, type_id)] :=
[i, {REFLECTOR}.field_static_type_of_type (i, type_id)]
i := i - 1
end
end
end
field_positions_table: HASH_TABLE [HASH_TABLE
[TUPLE [position: INTEGER; type_id: INTEGER], STRING], INTEGER]
once
create Result.make (1)
end
Using the feature update_object and assuming an object x has fields foo and bar of types INTEGER_32 and detachable STRING_8 respectively, the following code
field_values: HASH_TABLE [ANY, STRING]
do
create field_values.make (10)
field_values ["foo"] := {INTEGER_32} 5
field_values ["bar"] := " bottles"
update_object (field_values, x)
print (x.foo)
print (x.bar)
will print 5 bottles regardless of the previous state of the object x.

Sort issues with TArray<Myrectype> for large numbers

What is the reason why TArray.Sort<Mytype> does not work when I have large numbers in the comparison?
My code is as follows (Delphiy Tokyo):
Interface
Type
RCInd = record
Num : Integer;
Ger : Integer;
Confirmed : Boolean;
Total : Real;
End;
TArrInd = TArray<RCInd>;
Procedure SortInd (Var PArrayInd : TArrInd);
Implementation
Procedure SortInd (Var PArrayInd : TArrInd);
begin
TArray.Sort<RCInd>( PArrayInd,TComparer<RCInd>.Construct
( function (Const Rec1, Rec2 : RCInd) : Integer
begin
Result := - ( Trunc(Rec1.Total) - Trunc(Rec2.Total) );
end )
);
end;
......
When the values of Rec1.Total and Rec2.Total are within a Integer limits, this Sort works fine, BUT when values exceed Integer limits Sort procedure does not work! It generates a non sorted set of data in PArrayInd .
What is going on here?
The problem is one of overflow. The real values overflow the integer type.
The compare function is meant to return negative to indicate less than, positive to. Indicate greater than and zero to indicate equal. Your using arithmetic is the cause of your problem, leading as it does to overflow. Instead use comparison operators.
function(const Rec1, Rec2: RCInd): Integer
begin
if Rec1.Total < Rec2.Total then
Result := 1
else if Rec1.Total > Rec2.Total then
Result := -1
else
Result := 0;
end;
The problem in this question is trying to fit a real value into an integer, but even if you have integer data then arithmetic should not be used for compare functions. Consider the expression
Low(Integer) - 1
This results in overflow. As a general principle, always use comparison operators to implement compare functions.

Case insensitive array in Lua

I'm trying to program an addon for WoW (in lua). It's a chat filter based on specific words. I can't figure out how to get the array of these words to be case insensitive, so that any upper/lower case combination of the word matches the array. Any ideas would be greatly appreciated. Thanks!
local function wordFilter(self,event,msg)
local keyWords = {"word","test","blah","here","code","woot"}
local matchCount = 0;
for _, word in ipairs(keyWords) do
if (string.match(msg, word,)) then
matchCount = matchCount + 1;
end
end
if (matchCount > 1) then
return false;
else
return true;
end
end
Use if msg:lower():find ( word:lower() , 1 , true ) then
==> it lower cases both of the arguments to string.find: hence case insensitivity.
Also I used string.find because you probably want the 'plain' option, which doesn't exist for string.match.
Also you can easily return on the first word found:
for _ , keyword in ipairs(keywords) do
if msg:lower():find( keyword:lower(), 1, true ) then return true end
end
return false
Define keyWords outside of function. Otherwise you're recreating
table every time just to thorw it away moments latter, wasting time
on both creation and GC.
Convert keyWords to patter that match
both upper and lower case letters.
You don't need captured data
from string, so use string.find for speed.
According to your
logic, if you've got more than one match you signal 'false'. Since
you need only 1 match, you don't need to count them. Just return
false as soon as you hit it. Saves you time for checking all
remaining words too. If later you decide you want more than one
match, you still better check it inside loop and return as soon as
you've reached desired count.
Don't use ipairs. It's slower than simple for loop from 1 to array length and ipairs is deprecated in Lua 5.2 anyway.
local keyWords = {"word","test","blah","here","code","woot"}
local caselessKeyWordsPatterns = {}
local function letter_to_pattern(c)
return string.format("[%s%s]", string.lower(c), string.upper(c))
end
for idx = 1, #keyWords do
caselessKeyWordsPatterns[idx] = string.gsub(keyWords[idx], "%a", letter_to_pattern)
end
local function wordFilter(self, event, msg)
for idx = 1, #caselessKeyWordsPatterns do
if (string.find(msg, caselessKeyWordsPatterns[idx])) then
return false
end
end
return true
end
local _
print(wordFilter(_, _, 'omg wtf lol'))
print(wordFilter(_, _, 'word man'))
print(wordFilter(_, _, 'this is a tEsT'))
print(wordFilter(_, _, 'BlAh bLAH Blah'))
print(wordFilter(_, _, 'let me go'))
Result is:
true
false
false
false
true
You can also arrange this with metatables, in an entirely transparent way:
mt={__newindex=function(t,k,v)
if type(k)~='string' then
error'this table only takes string keys'
else
rawset(t,k:lower(),v)
end
end,
__index=function(t,k)
if type(k)~='string' then
error'this table only takes string keys'
else
return rawget(t,k:lower())
end
end}
keywords=setmetatable({},mt)
for idx,word in pairs{"word","test","blah","here","code","woot"} do
keywords[word]=idx;
end
for idx,word in ipairs{"Foo","HERE",'WooT'} do
local res=keywords[word]
if res then
print(("%s at index %d in given array matches index %d in keywords"):format(word,idx,keywords[word] or 0))
else
print(word.." not found in keywords")
end
end
This way the table can be indexed in whatever case. If you add new words to it, it will automatically lower-case them too. You can even adjust it to allow matching with patterns or whatever you'd like.

Delphi: using BigInts from a database

I´m using Delphi 7 with devart dbExpress to connect to SQLServer.
The problem is that when I add a bigInt field to a ClientQuery it comes as TFMTBCDField.
And the TFMTBCDField don´t have a method to get the 64 bit value.
I can use the Field.AsVariant or the StrToInt64(Field.AsString) to pick this 64 bits value.
Is there a better way to pick/use this value?
Maybe add a TLargeIntField manualy to dataset, set it's FieldName to appropriate name and use such code:
SomeInt64Value := (qryMyQuery.FieldByName('blahblah') as TLargeIntField).AsLargeInt;
Do not remember exactly types, but it worked this way in Delphi6.
You can convert the BCD to Variant and than to int64 with VarFMTBcdCreate from unit FMTBcd.
Try this:
var value64 : int64;
...
value64 := VarFMTBcdCreate(Field.Value);
The data format for TFMTBCDField is the TBcd record from the FMTBcd unit. You can get that raw value by reading the field's Value or AsBCD properties.
Depending on what you need the value for, TBcd might be sufficient. That is, you might not need to convert it to an Int64. The FMTBcd unit provides functions to add, subtract, multiply, and divide TBcd values.
The unit provides no conversions to Int64. There are conversions to Variant, string, Currency, Double, and Integer. If we were going to write an Int64 conversion, the Integer conversion is probably a good place to start, so let's take a look at how it's implemented:
function BcdToInteger(const Bcd: TBcd; Truncate: Boolean = False): Integer;
var
ABcd: TBcd;
begin
if Truncate and (BcdScale(Bcd) > 0) then
NormalizeBcd(Bcd, ABcd, Bcd.Precision, 0)
else
ABcd := Bcd;
Result := StrToInt(BcdToStr(ABcd));
end;
So, the VCL itself doesn't provide any more direct way to convert a TBcd to an Integer than to go through string. Therefore, it looks like your idea to call StrToInt64 on the string version of the field is fine.
I dont have Delphi 7 installed here anymore, but looking in the help, I see you can get as Float (Double), like this:
function GetFieldAsInt64(Field: TField): Int64;
begin
Result:= Int64(Round(Field.GetAsFloat));
end;
And then, call the function:
var
Value: Int64;
begin
Value:= GetFieldAsInt64(MyFMTBCDField);
end;

Resources