Closed BookOwl closed 7 years ago
So I've made some progress on implementing some C for lists, but I'm not really sure what to do next. :/
I think some sort of Haumea framework for C is needed for this to really work out - for example, right now there is no good way for the C I've written to handle what happens when somebody tries to access items out of bounds.
The other problem is that using C's type system directly won't work out too well, as there are no general methods for things like comparison - this means that I can't write something like an indexOf(item) method for lists, because it would fail on strings, which require a different method of comparison.
I think what will end up happening is that some sort of general system will evolve that provides some set of methods for some particular type, as well as info about whether that type supports certain methods, so probably something like:
/* The 'int' return type is used instead of boolean here */
int int_equals(int a, int b);
int int_supports_equals = 1;
int int_gt(int a, int b); /* 'greater than' */
int int_supports_gt = 1;
...
int string_equals(string a, string b);
int string_supports_equals = 1;
int string_supports_gt = 0;
Although, even this might not work if types become more general.
One other problem is that with the way lists currently work, like:
list_int *myInts = list_int_empty();
for(int i = 0; i < 10; ++i) {
list_int_add(myInts, i);
}
list_int_free(myInts);
lists of certain types won't work, simply due to the restriction on length of C names.
/* Some c compilers won't be able to tell these apart: */
list_list_list_list_list_list_int *myUglyList;
list_list_list_list_list_list_list_int *myUglierList;
Actually, I'm now thinking that the best way to do this may be to implement lists through casting - each list merely has a void pointer for its data, and it's casted whenever it's accessed.
That way, the type name wouldn't really matter, and lists could stack infinitely.
What do you guys think of this syntax?
variable x is a Type /* Declare a variable's type */
to foo ... /* Declares a procedure that returns an Integer */
to bar ... returns a String /* A function that takes zero arguments and returns a String */
/* A function that takes an Integer and a String and returns a String */
to replicate with (times is an Integer, str is a String) do
if times = 0 then return ""
else return str + replicate(times - 1, str)
end
returns a String
Or how about putting the prototype on a different line?
function replicate takes an Integer, a String, and returns a String
to replicate with (times, str) ...
If no prototype is found for the function, it assumes Integer for all the types involved.
Asking @liam4 @nanalan @jonathan50 @algmwc5 @jupiterio @joker314 @tjvr and @Firedrake969 for input
Return syntax is a bit weird, and I don't like the seperate prototype idea. To be honest, I still like
string x
set x to "foobarbaz"
display(replicate(4, x))
/* foobarbazfoobarbazfoobarbazfoobarbaz */
to replicate with (integer times, string str) do
if times = 0 return ""
else return str + replicate(times - 1, str)
end
But I'm still unsure on defining the return type.
The return type is the only thing that I'm not quite sure how to define either.
Maybe considering how functions are defined could be different. If possible, all functions could be lambdas?
set replicate to string with (integer times, string str) return "no"
Is the normal, C-style type declaration not good enough?
integer x
string hello
set x to 1
set hello to "Hello"
/* or even: */
set integer answer_to_everything to 42
Functions are, I think, the hardest to deal with. It's possible they could have type inference:
to factorial with (integer n) do
if n = 0 return 1 /* returns an int because below statement returns an int */
else return n * factorial (n - 1)
end
However I think this is potentially really confusing. I still think "as" is not a bad choice:
to factorial with (integer n) as integer do
/* or */
to factorial as integer with (integer n) do
@nanalan I don't think to
makes sense without a name following it. But to
could be syntactic sugar for declaring a variable and assigning a procedure created with a lambda expression to the new variable?
@TheMonsterFromTheDeep, "as" is definitely the best choice for return types. I'm not so sure about the C style variable declarations.
What do you guys think about the prototype idea?
I don't like the seperate prototype idea.
I'm all for @TheMonsterFromTheDeep's ideas.
@BookOwl looks like everyone (bar you) likes @TheMonsterFromTheDeep's ideas - time for implementation?
Sure. You can decide if you want to use to factorial with (integer n) as integer do
or to factorial as integer with (integer n) do
. I like both the same amount
We can support both @BookOwl if we really want - it's a matter of preference.
Let's just use to factorial with (integer n) as integer do
then.
/* Variables */
string x
integer y
float z
/* Function types */
to foo do ... /* If there are no parameters and no return type, Haumea assumes an integer return type */
to blah as string do ... /* But you can change it */
to baz with (integer n, string s) as string do ... /* Declaring the argument and return types */
Haumea assumes an integer return type
Surely it should assume that it returns none
?
Also global variables with global
?
set global string world to "Earth"
display("Hello, " + world)
Haumea assumes an integer return type
Surely it should assume that it returns
none
?
In haumea functions that don't return anything return the integer 0.
That feels a little unintuitive..
We should have booleans too, don't you think?
set boolean i_can_use_haumea to true
What do booleans have to do with default return values?
I meant them as different things - why not have a none
type which cannot be assigned to a variable? e.g.
to nine_plus_ten do
display(21)
/* implicitly returns none */
end
set twenty_one to nine_plus_ten() /* compiler error, variable cannot be `none` */
That sounds good to me.
Adding this to the spec now.
For global variables, I think that any variables declared at the top level should be global, and variables declared in a function should be local.
integer x /* This is a global */
to blah do
integer y /* This is local */
end
to baz do
integer x /* Shadows the global variable */
end
Defining a global variable inside a local scope can be helpful though.
When?
Sorry, I meant assigning a global variable. Nevermind :stuck_out_tongue_winking_eye:
This all looks good, but isn't it easier to say that if a function doesn't return anything it must only be used as a procedure, instead of making up a none type that can't be used?
a procedure
That's basically it. Being none
, nothing can be set to its result.
Continuing discussion from https://github.com/haumea-lang/haumea/issues/4