I want to instantiate a package and pass functions to it that overload operators in order to make a Generic Binary Search Tree. Here's the specifications.
bstgen.ads (snippet)
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Unchecked_Deallocation;
GENERIC
type Akey is private;
type TreeRecord is private;
with function "<"(K: in Akey; R: in TreeRecord) return Boolean;
with function ">"(K: in Akey; R: in TreeRecord) return Boolean;
with function "="(K: in Akey; R: in TreeRecord) return Boolean;
PACKAGE BSTGen IS
TYPE St10 IS NEW String(1..10);
TYPE TreePt IS PRIVATE;
PACKAGE EnumIO IS NEW Ada.Text_IO.Enumeration_IO(Akey); USE EnumIO;
driver.adb (snippet)
with Ada.Text_IO; use Ada.Text_IO;
WITH BSTGen;
PROCEDURE Driver IS
IP: Integer := 1;
TYPE Names IS (Resig,Keene,Marsden,Vuijic,Marcus,Gonzalez);
PACKAGE NamesIO IS NEW Ada.Text_IO.Enumeration_IO(Names);
type St10 is NEW String(1..10);
type Customer is
record Name: Names;
PhoneNumber: St10;
end record;
function "<"(K: in Names; R: in Customer) return Boolean is begin
return K < R.Name;
end "<";
function ">"(K: in Names; R: in Customer) return Boolean is begin
return K > R.Name;
end ">";
function "="(K: in Names; R: in Customer) return Boolean is begin
return K = R.Name;
end "=";
PACKAGE IntIO IS NEW Ada.Text_IO.Integer_IO(Integer); USE IntIO;
PACKAGE mybst is NEW BSTGen(Names,Customer,<,>,=); USE mybst;
R, Pt: TreePt;
Name: Names;
Phone: St10;
Status: Boolean;
BEGIN
R := CreateTree;
Pt := R;
However, when I try to compile, this is the output:
driver.adb:24:04: expect subprogram or entry name in instantiation of "<"
driver.adb:24:04: instantiation abandoned
bstgen.ads:19:53: expect discrete type in instantiation of "Enum"
bstgen.ads:19:53: instantiation abandoned
This includes a bunch of errors stating that driver.adb's methods are not visible, which is to be expected as the instantiation of mybst is abandoned. How do I fix this?
In bstgen.ads, you say
PACKAGE EnumIO IS NEW Ada.Text_IO.Enumeration_IO(Akey);
but in the generic formal part you said
type Akey is private;
which means that the compiler can assume very little about the actual type except that equality and assignment are available. It could be a record of hundreds of bytes; it certainly need not be an enumeration.
To ensure that Akey is an enumeration, you need to say
type Akey is (<>);
as in ARM12.5(13).
In driver.adb, you say
PACKAGE mybst is NEW BSTGen(Names,Customer,<,>,=);
which should read
PACKAGE mybst is NEW BSTGen(Names,Customer,”<“,”>”,”=“);
Related
Is overloading of implicit type conversion possible for enumerations in Delphi?
program TriState;
type
TTrilean = (trNone = -1, trFalse = 0, trTrue = 1);
TTrileanHelper = record helper for TTrilean
public
class operator Implicit(...)...; //E2123 PROCEDURE, FUNCTION, PROPERTY, or VAR expected
end;
var
v: TTrilean;
begin
v := trNone;
v := True; //Would like to have class operator Implicit here
end.
In the Delphi 10.4.2 documentation I read:
Note: Class and record helpers do not support operator overloading.
For example, I have an abstract type for the base type, and I want to implement a type factory that dynamically creates concrete types under this abstract type, with names (and other type traits) given by user input arguments.
abstract type AbstractFoo end
function TypeFactory(typename::String, supertype::DataType)
...
return ConcreteSubtype
end
The function TypeFactory takes typename and supertype arguments and creates a concrete type that belongs to the supertype and has the same name as typename.
I know this kind of class factory is not too difficult to implement in Python (e.g., How can I dynamically create derived classes from a base class). In Julia, it can be imitated by using eval(parse()) to evaluate string expressions (Is it possible to create types in Julia at runtime?). But it looks to me that this solution is not a true type factory in an object-oriented sense. Is it possible to have a type factory in Julia that behaves like those in OOP languages (Python, C#, etc.)?
Edit [8 Feb 2018]:
My bad for not expressing things clearly. I'm new to Julia and had just recently started coding my project in it. I knew that inheritance is not supported and did not meant to get around with that in Julia.
Coming from a Python background, I had some feeling that eval() is usually meant for prototyping but not for production code. Of course Julia is different and is far more efficient than pure Python, but the code given to eval() still has to be compiled at the runtime (correct me if I'm wrong). And its use is sorted of discouraged too, from the perspective of performance (Julia, speeding up eval).
And by 'user input' I didn't mean solely command-line input. They could be supplied by a user's config file. (That being said, #SalchiPapa's macro solution is both apt and elegant!)
Is it possible to implement a type factory in Julia without using eval()?
You could use a macro:
https://docs.julialang.org/en/stable/manual/metaprogramming/#man-macros-1
Macros provide a method to include generated code in the final body of a program. A macro maps a tuple of arguments to a returned expression, and the resulting expression is compiled directly rather than requiring a runtime eval() call.
julia> VERSION
v"0.7.0-DEV.2098"
julia> module Factory
export #factory
macro factory(type_name::Symbol, super_type::Symbol)
# ...
return quote
struct $type_name <: $(esc(super_type))
# ...
bar
end
return $(esc(type_name))
end
end
end
Main.Factory
julia> using Main.Factory: #factory
julia> abstract type AbstractFoo end
julia> #factory ConcreteFoo AbstractFoo
ConcreteFoo
julia> foo = ConcreteFoo(42)
ConcreteFoo(42)
julia> foo.bar
42
julia> ConcreteFoo <: AbstractFoo
true
julia> supertype(ConcreteFoo)
AbstractFoo
Edit according to #Gnimuc understanding in the comments, using input:
julia> module Factory
export #factory
function input(prompt::String="")::String
print(prompt)
return chomp(readline())
end
macro factory(type_name = input("Type name: "))
AbstractT = Symbol(:Abstract, type_name)
ConcreteT = Symbol(:Concrete, type_name)
return quote
abstract type $(esc(AbstractT)) end
struct $ConcreteT <: $(esc(AbstractT))
bar
end
return $(esc(AbstractT)), $(esc(ConcreteT))
end
end
end
Main.Factory
julia> using Main.Factory: #factory
julia> #factory
Type name: Foo
(AbstractFoo, ConcreteFoo)
julia> #factory
Type name: Bar
(AbstractBar, ConcreteBar)
julia> #factory Baz
(AbstractBaz, ConcreteBaz)
julia> foo = ConcreteFoo(42)
ConcreteFoo(42)
julia> foo.bar
42
julia> ConcreteFoo <: AbstractFoo
true
julia> supertype(ConcreteFoo)
AbstractFoo
julia> #macroexpand #factory
Type name: Qux
quote
#= REPL[1]:13 =#
abstract type AbstractQux end
#= REPL[1]:14 =#
struct ConcreteQux <: AbstractQux
#= REPL[1]:15 =#
bar
end
#= REPL[1]:17 =#
return (AbstractQux, ConcreteQux)
end
julia> eval(ans)
(AbstractQux, ConcreteQux)
In Rascal, how do I introduce a name for a composite type (that's constructed from built-in basic types and type constructors like list and map)?
E.g. I'd like to do something like
typedef IntList = list[int];
typedef StrToIntList = map[str,IntList];
StrToIntList mymap = ();
Instead of typedef you can use alias. For instance:
rascal>alias IntList = list[int];
ok
rascal>alias StrToIntList = map[str,IntList];
ok
rascal>StrToIntList mymap = ();
StrToIntList: ()
You can use alias to give alternate names to existing basic types as well, so you could also do something like:
alias ID = int;
And, you can include type parameters if needed, so a graph over an arbitrary type could be defined as:
alias Graph[&T] = rel[&T,&T];
Note that an alias introduces a type equivalence and not just a sub-type. So for any function that accepts the alias as argument type, you can also provide a value of the type that it aliases, or any of its sub-types.
Do you know why when declaring local const vars, the script cannot compile?
Sorry, I know very little pascal and cannot figure out why this is not working!
This example (see function CircleArea) shows that my syntax should be fine.
http://www.tutorialspoint.com/pascal/pascal_quick_guide.htm
This is what I am trying to do:
//---placed within [Code]
procedure MyLog(const _functionName, _msg: String);
begin
Log(_functionName + '(): ' + _msg);
end;
function MyExec(const _filename, _params, _dir: String): Boolean;
const // <--- compilation fails in this line!
MethodName = 'MyExec';
var
ResultCode: Integer;
begin
MyLog(MethodName, _filename);
// ... invoke Exec(), etc. ...
end;
//---
You were trying that right. If Inno Setup were using Pascal it would even work, but since it is based on a custom Pascal Script language with limitation on declaring local constants, you can't do it that way. You must define your constant globally instead:
[Code]
const
MethodName = 'MyExec';
function MyExec(const _filename, _params, _dir: String): Boolean;
var
ResultCode: Integer;
begin
MyLog(MethodName, _filename);
// ... invoke Exec(), etc. ...
end;
You can use #define to define "local" constants and then use ExpandConstant('{#ConstantName}') whenever you need the value of that constant.
function MyExec(const _filename, _params, _dir: String): Boolean;
var
ResultCode: Integer;
begin
#define MethodName 'MyExec'
MyLog(ExpandConstant('{#MethodName}'), _filename);
// ... invoke Exec(), etc. ...
end;
Note that the constant isn't actually local, because it is just a preprocessor definition. So the functions after MyExec could refer to the constant as well, which might be a potential cause of error (but isn't worse than defining a real global constant). You can redefine the same constant in another function or procedure without causing a compile error though.
function MyExec(const _filename, _params, _dir: String): Boolean;
begin
#define MethodName 'MyExec'
MyLog(ExpandConstant('{#MethodName}'), _filename);
end;
function MyExec2(const _filename, _params, _dir: String): Boolean;
begin
// if you forget to put the next line, it will log 'MyExec'!!!
#define MethodName 'MyExec2'
MyLog(ExpandConstant('{#MethodName}'), _filename);
end;
For added safety, you might want to #undef the "local" constant at the end of the function, in which case InnoSetup would cause a compile error if you'd try to reference it in the next function.
function MyExec(const _filename, _params, _dir: String): Boolean;
begin
#define MethodName 'MyExec'
MyLog(ExpandConstant('{#MethodName}'), _filename);
#undef MethodName
end;
function MyExec2(const _filename, _params, _dir: String): Boolean;
begin
// Forgot to define MethodName, but InnoSetup will cause an error
// "undeclared identifier: MethodName".
MyLog(ExpandConstant('{#MethodName}'), _filename);
end;
This is what I want I am trying to parse
type Number(); // define a type called "Number" with no member variables
type Point(Number x, Number y); // define a type called Point with member variables
// and something with generic types
type Pair[T, V](T first, V second);
// and even something cyclic:
type LinkedList[T](T payload, LinkedList[T] rest);
And here's my xtext grammar that allows it:
TypeDecl returns SSType:
'type' name=TypeName
('[' typeParams += TypeName (',' typeParams += TypeName)* ']')?
'(' (args += Arg (',' args += Arg)*)? ')' ';'
;
TypeName returns SSTypeName:
name=ID
;
Type:
tn = [SSTypeName] ('[' typeParams += Type (',' typeParams += Type)* ']')?
;
Arg:
type = Type argName = ID
;
Which works, but is way too liberal in what it accepts. If something is declared as a generic (e.g. the LinkedList in the above example) it should only be valid to use it as a generic (e.g. LinkedList[Number] and not LinkedList) and ideally the arity of the type arguments would be enforced.
And of course, if something is declared to not be a generic type (e.g. Number), it shouldn't be valid to give it type arguments.
Example of stuff it will wrongly accept:
type Wrong1(Number[Blah] a); // number doesn't have type arguments
type Wrong2(Pair a); // Pair has type arguments
type Wrong3(Pair[Number, Number, Number] a); // wrong arity
Any suggestions, comments, code or tips on how to do this properly would be much appreciated.
You should enforce the correct number of type arguments in your validator. It often better to have a liberal scope provider and a strict validator to provide better error messages.