Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - shkaboinka

Pages: [1] 2 3 ... 6
1
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: November 17, 2017, 02:30:10 pm »
It's hard to explain the whole thing, but I can explain what I mean by escaping "language" (or at least escaping compiled language):

Instead of writing a program in language-A or language-B and being limited to using only the features of one language, you should instead be able to mix and match whatever language features you want, even ones that you create yourself. Or you should be able to modify those features just for your program. So how do you do this?

Instead of using (or writing) a compiler for some language, you write your own program to generate the program you want. This would be more doable if you take the code that normally would be in a compiler, and instead make it part of a library. A compiler creates structural representations (AST) of the code that it compiles, so instead the library would be a tool to build parts of that representation and give them back to you. You could then modify them, or create your own somehow; and then you hand them back and say "ok, build the program from these".

Think of uncompiled source code (written in a compiled-language) as a program that makes an executable program, and the compiler as the interpreter of that program. When viewed in this way, declarations within code (classes, functions, variables) act as imperative commands to create components of a program. Replacing these "commands" with an API & user-defined functions would allow programs to be generated with any components & mechanisms, rather than from a fixed set of "language features". No more language restrictions!

::instead of this:: class Foo { Bar b }
::you write this:: myClass = MakeClass("Foo"); myClass.AddMember("b", myBarClass)

::another example:: myTrait = new MyVeryOwnTraitsImplementation("ResizableThing"); myTrait.ApplyTo(myClass)

This is what makes languages like JavaScript so powerful: many things that are "declarations" in other languages are actually imperative calls to user-defined functions. This is why people have been able to make so many different kinds of object-models (etc.) in it. This makes things like compiler hooks and reflection mostly obsolete.

----

Now you might notice that the program that is generated by your program has whatever features and structure you want; but what about the program that did the generating; what language is that written in? Two possible answers are:
(1) It doesn't really matter, so long as you can generate whatever you want from it.
... However, since it just has to "run" once, it is more important for that code to have good expression than for it to be efficient, and that screams "interpreted language". Also, why would you want to have your source program, feed it into another program called a compiler, which generates an executable program, which you run to have it generate the program that you care about? Why not just run an interpreted program that generates the output program? So though it really does not matter what language the program-generating-program is written in, a compiled-language has no gain here, but the advantages of an interpreted language do apply.
(2) The only other option is to "close the loop" with some meta-circularness. That is, a software system or tool that is self-defining, self-modifying, etc., which is only possible if everyrthing (interpreter, code structure, etc.) is accessible at runtime. That is what my Objects project is about, except that it will do more than just that (e.g. a UI through which all things can be viewed or changed at runtime by the user, including the workings of the UI itself).

2
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: November 02, 2017, 04:04:36 pm »
Great to hear this isn't dead. I'm still quite excited to see how programming in this language is gonna be :)

I regret to inform that this is dead; but mainly because I have something better.
I hope you'll be excited to see what programming (or computering at all) in a fully-dynamic system is like.

To save time, I'm going to paste what I've said elsewhere about the end of Antelope and the start of SomethingNew (2 posts).





(Pasted from elsewhere):

Sorry for abandoning this, especially after all that went into it. This is mainly because life happened. However, there is much learning/thought to reflect on; so here are some of my reflections:

1. Top features I've identified that I'd love to have in a language:
A  - First-order functions & lambda syntax (C# linq queries simplify even further!)
B - Functions that can yield values and resume from the same point (aka "coroutines", "generators", or C#'s "enumerators").  My "cofunctions" (not the same as the mathematical term) took this a step further by reifying the function-object ("functors" in other languages) allowing direct inspection/mofification of the entry-points and current "position", and also letting these function-objects to be emedded within other objects with full access to other members and vice versa.
C - Code that is interpreted / pre-compiled at compile time. I found this is BEST done in terms of all the built-in language features, rather than providing a separate syntax/language JUST for interpreted code. LISP does this by allowing macros that have the full power of the full language & other code. The D language does this very well by allowing meta-operations on the code using regular D code itself.
D.i - Use of "interfaces" to allow objects of ANY type to be used somewhere, so long as it has the correct members (Google's "Go" does this well). This provides the feel of dynamic "duck" typing, but with compile-time static type-checking.
D.ii - Ability to provide "default" members of interfaces.
E - "Traits" - that is, reusable blocks of code (object definitions) that can be combined statically, but WITHOUT introducing an inheritance relationship; it's just a clean mechanism for code-reuse. Traits are more refined than "Mixins", because you have full control over how to resolve conflicting names when combining multiple traits, and you can also map things differently (e.g. use a trait that provides behavior X and requires behavior Y, but provide "Z" in place of Y, or take X but call it "W"). Scala's "traits" are actually Mixins, because they do NOT allow any control over mappings, and they manipulate runtime inheritance hierarchies rather than combine code statically.
F - Reflection using "attributes" (as in C#; "annotations" in Java). This essentially treats the parts of a program like objects, allowing you to attach custom properties and behaviors to parts of the AST (e.g. to a "class"), without any impact on the RUNTIME entities that the relate to. In C#, this is done by creating runtime representations of everything (e.g. you can inspect classes and their members dynamically at runtime); but I was having this all be accessible at RUNTIME. That is code, code that can manipulate / be conditional on other code at compile-time. The D language does this very well. Languages that are self-hosting ("bootstrapped") like LISP or REBOL do not NEED reflection, because everything is already in the same "layer".
G - Default arguments for functions. My special technique for this is to insert the assignments of default values as extra instructions at the top of the function, and have multiple entry points in the function. To use all defaults, the function is entered at the "top"; when one default value is overridden, the function is entered "after" that default assignment. This not only saves code space, but allows the function to be referenced as EVERY sub-set of function type by referring to the correct entry point (e.g. F(A, B=X) is an F(A) and an F(A,B)). C# handles defaults by manually inserting the default values at all the call-sites, which means that the calling code can be "wrong" if the actual function-definition is separately recompiled with different defaults.
H - Function closures. I accomplished this through the "cofunction" implementation (B), though I had decided to remove them as an explicit language feature and instead have the compiler "detect" when to use them. That is, any function declared inside another function or class that references contextual data would automatically create a function-object to track it (or embed this data in the parent object, as applicable).
I - Generalization between functions and methods. Most languages do this by making methods a generalization of functions (e.g. the D language) by having the "this" argument count as the "first" argument (e.g. an O.F(A,B) is also an F(O,A,B)); but I did this by making functions a generalization of methods (e.g. an F(A,B) is also an <null>.F(A,B)) by reserving a special register (IX for z80) for passing the "this". This allows something that takes an F(A,B) to take an O.F(A,B) (JavaScript allows this by resolving "this" separately the function arguments). This also ties into J:
J - Generalizing all aspects of a source program into an object model. That is, a "class" and its static members are actually stored as an "object" which can be passed around or even inherit from other classes. Namespaces (including the default one) are also objects like this, and so all global/namespace-level declarations are members of some object (and thus all functions are "methods" -- but the passing of "this" is optimized out of the compiled code, because the allocation of these namespace "objects" is statically determined and thus so are it's members). This also allows one to define the namespace of a whole program (or part of one) to inherit from another class or compose traits into it, thus bringing OOP facilities to the level of the whole program.

...The above started as a 3 item list of 3 sentences. I forgot how much I put into this!...

2. I'm glad to see other languages/tools available, even though I didn't finish this one.
When I started, there was NOTHING like this, and mostly I really wanted to introduce a more general/full/open-ended language than the built in TI-BASIC (without assembler being the only other choice) so that others could experience the joy of programming on a fully-programmable device that is accessible to many. ... But since then, there is AXE, GRAMMER, and perhaps others, and that makes me happy and reduces my urgency. Big props to Xeda and Kevin for that!

3. ...I've had a bit of a perspective change on "language", which makes it hard to want to invest too much into one. I've been following past/present key figures such as Jim Coplien, Rich Hickey, Bret Victor, Alan Kay, Christopher Alexander, Richard P Gabriel, etc. (I actually found a GOLD MINE with lots of videos from many of these guys), and traced some of the roots of OOP and modern software paradigms to their origins to find that much of current view of what software & programming is, is a reduced form of a once grander picture. I've been one of many to try to find the next better language, paradigm, tools, etc. ... but this is just the software culture chasing its own tail. What needs to change is how software ITSELF is thought of, and the tools we use to do that cannot do that for us. My first wake-up call was recognizing this delimma as called out by Jim Coplien (and it took a year of study to for me to finally see what he's talking about), but does an excellent job of pointing this out (and demonstrating this could change), as does Rich Hickey (especially if you consider his statements of simple representation in a GENERAL sense, rather than just promoting functional programming or Clojure), and Alan Kay has a lot to say, but is very abstract. ... You cannot language or unit-test or TDD or tool your way into good software design; you have to think about software design itself, which means understanding HUMAN models of software. THAT should shape language & programming paradigm, but instead the software industry has let the opposite happen. ... places like THIS do a lot of good though in supporting open-ended exploration & learning; but there are nonetheless some DEEPLY ingrained views that come with being a programmer nowadays, and a lot of it is upside-down, but it's hard to change or even challenge.

... I see a world of software in which "language" becomes a malleable thing that can be adapted and mixed selectively or on the fly, rather than as a rigid distinction between one language or another. Ideally, you want to be able to create whatever software you want using whatever paradigms and language tools you want, without having to have had someone package it up into this static thing called a "language". Less like choosing between Legos or K'nex and more like being free to use any of all of paint, markers, crayons, pencils, charcoal, clippings, staples, glue, etc. etc. on your paper, and not even being limited to those things, or even to paper.



A lot of this was me trying to make the "perfect" general purpose language, and I was never satisfied with it 100%, so it never got done. Now that I have kids and a career, I see no priority for something this big; though I enjoy the discussion, and maybe someday I will use these ideas somehow. I've also had some changes of heart as to what to pursue in software, it's put me off of pursuing "language" too strongly. It's like Jim Cope says about language.

Anyway, I'm still all about the ideas, but I have nothing to offer beyond that at this time.

As for "the old one" (I assume you are talking about Antidisassemblage / SquirrelBox), yes that was a precursor. It was largely a first-time crash-course attempt at a language & compiler (which I'm still somewhat proud of); but over time I've learned that I could do SO much better! ... But I don't know if it will come to be in any time span that anyone is willing to wait for.

Anyway, I'll add further thoughts to my previous post (which I left "in progress")

Thanks for following my work though!





If anyone is interested in what I "moved on" to, here it is: https://github.com/d-cook/Objects

The idea is a software system that escapes the rigid boundaries of traditional languages and software tools, by empowering the end-user to create whatever things & behaviors they desire. It will act as a tool with a UI for creating and manipulating data & code in the form of objects, including all parts of the system itself (interpreter, UI, etc.). This will work the system will be made out of the same components that it edits, which can all be live-edited.

One idea for how this can be used is to replace traditional software development (code in language-X, depends on compiler- or IDE-Y, etc.), a software project can be its own self-contained tool that is its own editor, compiler, and UI for whatever software is being developed. All these pieces can be used on each other, and you can thus create the ideal everything (language, UI, etc.) for the thing being developed, and it's all just part of "the code". And it need not be uniform through-out (e.g. a chunk of code that contains its own interpreter or UI just for that part). That is one example, but the possibilities are endless.

...

Beyond that, as I am not the only one doing "this kind of stuff", I've actually formed a collaboration with others who are seeking to redefine software in similar ways. Check it out (it has its own brief explanation / history): https://github.com/d-cook/SomethingNew

Edit (Eeems): Merged triple post

3
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: October 17, 2014, 02:28:01 am »
... So it's been quite a while (getting married, having two kids, and a dog, and starting a career tends to change priorities), but I've been keeping this project going occasionally, and paying a lot more attention to it in recent months. There have been LOTS of changes (again), and I've been posting on Cemetech and made lots of updates to the language grammar tinyurl.com/z80antelope (though everything else is way out of date). Maybe sometime I'll post a synopsis of what has changed, and bring this thread back life, too!

Anyway, I do intend to (eventually) finish this, and at least have fun along the way discussing and sharing my thoughts/adventure in programming language design :)

4
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: September 29, 2012, 12:02:27 pm »
Clarification of changes:

 * Namespaces will be allowed to have type-parameters.
 * Constructors will be as just explained in previous posts.
 * "Virtual functions" are stored directly in their container as function-pointers.
 * Functions can be mapped to other functions without relisting arguments (we can discuss syntax: "func x = y" or "func x(args) = y").
 * Interface functions may have default bodies; Interfaces may also contain data members (stored "by value"), which may have default values. This might result in the "func" keyword being required in interfaces again though.
 * CONSIDERATION: I'd like to remove "anonymous members" from structs, and instead (1) Allow datatype names to be used as variable names, and (2) Have structs automatically "inherit" the properties (i.e. members and functions) of ALL their inner members (with any conflicts having to be resolved explicitly):

Code: [Select]
// OLD:
struct A { int x, y; }
struct B { A; int y; }
b := B(A(1,2),3);
// b.x == b.A.x == 1
// b.A.y == 2
// b.y == 3

// NEW:
struct B { A A; int y; }
// ...Everything else is the same

5
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: September 28, 2012, 02:10:48 pm »
It will probably be a while: Now that I have a full-time software development job and a newborn baby, this project is officially on the back-burner (though I still poke at it between the cracks when I can).

6
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: September 27, 2012, 06:39:50 pm »
I've decided to forego the idea of traits for Antelope. It's a great idea, but I found that most of the same expressiveness is already available from the fact that methods are declared separately, interfaces are applied automatically, and parametric types fill in the rest.

Some other changes I may make:
 * Namespaces may have type-parameters (namespace Foo<T>;) so as to build setups more generically (not only would you not have to do this to every method, but you could also have static/global T values).
 * Optional Constructors: This would change construction to use parenthesis, and optional constructors (and destructors?) would be declared within structs:
Code: [Select]
struct Foo {
   int x; int y;

   new(int xArg, int yArg = 5) { // Constructor
      x = xArg;
      y = yArg + x; // Now you can map values differently
   }

   delete { ... } // Destructor
}

f1 := Foo(1,2); // x=1, y=1+2
f2 := Foo(1); // x=1, y=1+(5)

struct Bar { int x,y,z; } // Constructors not required.
b := Bar(1,2,3);
(Another option would be Kotlin's approach)

Some other ideas I'm toying with:
 * Changing lambda syntax from (args)=>code to { args => code } (and from =>code to { code }). I'd only do this so I could allow these to come AFTER function-calls when it is the last argument (Kotlin does some fancy stuff this way). Call(1,2,=>{code}) would become Call(1,2) { code }
 * Singleton objects (e.g. "object x { ... }" declares x like a struct, but that x is also the one and only instance of it)
 * "Class (struct) objects" as in Kotlin - a struct may contain a singleton object, which is referred to externally by the class name itself. This can be used to encapsulate "static members", but be referred to as a variable. This object can compose (inherit) from another struct for added functionality.
 * Change variable syntax so that types come after a colon (func f(x:int, y:int, z:int = 3) { ... }). This matches how return-values are given, and would make parsing types much easier (which is also true with constructors, since then Foo{1,2} becomes Foo(1,2) and can be parsed as a function-call). Also, this makes "A := B" more sensible for type-inference, since the type is "left out".

The only other thing I am debating is whether "virtual" functions should be stored directly within structs, or accumulate in an array as a "vtable" (C++) aka "class descriptor" (Java). I'm wanting to just keep them as pointers, because I want the language to be VERY oriented around using interfaces INSTEAD of using that kind of "inheritance". This allows embedded function-pointers to count as methods, and it is still POSSIBLE to construct a vtable (e.g. to mirror some hypothetically already compiled setup if necessary).

Thoughts?

7
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: September 27, 2012, 06:21:01 pm »
(I posted this elsewhere a while ago) Potential Major Change:

TRAITS seem to solve most of the problems not solved (or caused) by inheritance (single or multiple, or using mixins). Traits are like interfaces, except you can provide default implementations for functions ("methods"). They would keep their original functionality as interfaces without any added bulk or framework, but with the addition that structs can "use" them and gain all their methods for free! This provides a clean model for code reuse without affecting any underlying framework!

Code: [Select]
trait Wargler {
  Foo(int);
  Bar():int;
  Wargle() { Foo(Bar()); }
  Talk() { Print("Wargle!"); }
}

trait Fargler {
  Boo();
  Bar() = Moo(5); // { return Moo(5); }
  Fargle() { Moo(Bar()); }
  Talk() { Print("Fargle!"); }
}

struct A : Wargler { ... }
struct B : Fargler { ... }
struct C : Wargler, Fargler { ... }

In this example, structs A, B, and C use above traits, causing the implemented methods (functions) of those traits to be automatically added as (static) methods of those structs. The struct is required to implement the other methods itself (and may also re-implement any of the others). Multiple traits can be combined (as with struct C), and implement each-other's methods (but if more than one trait implements the same method, then it must be redefined in the struct).

The following methods are required for this trait usage:

Code: [Select]
func A.Foo(int) { ... }
func A.Bar():int { ... }
func B.Boo() { ... }
func C.Foo(int) { ... }
func C.Bar():int { ... }
func C.Boo() { ... }
func C.Talk() { ... } // Overlaps both traits

The following methods are generated automatically from this trait usage:

Code: [Select]
func A.Wargle() { Foo(Bar()); } // func Wargle(A* a) { A.Foo(a, A.Bar(a)); }
func C.Wargle() { Foo(Bar()); } // ...etc...
func B.Fargle() { Moo(Bar()); }
func C.Fargle() { Moo(Bar()); }
func A.Talk() { Print("Wargle!"); }
func B.Talk() { Print("Fargle!"); }

Traits still work like interfaces:

Code: [Select]
func Warg(Wargler w) { w.Wargle(); }
func Farg(Fargler f) { f.Fargle(); }

(a,b,c) := (A{...}, B{...}, C{...});

Warg(a); // wa := Wargler{a}; Warg(wa);
Farg(b); // ...etc...
Warg(c);
Farg(c);

Traits can build off of each other in the same manner (e.g. trait X : traitY { ... }). Trait methods can also be aliased to avoid collisions or link to different methods (e.g. "struct X : Wargle(Other = Foo)" gives "Other" as an alias for Wargle's "Foo" method within struct X).

Note: I have also added that syntax as a short-hand for a function body which just returns a value (i.e. "= value" rather than "{ return value; }").

8
TI Z80 / Antelope - New Syntax for OOP Idioms!
« on: August 09, 2012, 04:19:58 pm »
NEW SYNTAX FOR OOP IDIOMS!

To test the feel of everything, I compared a simple OOP example from Java/C# with the "equivalent" Antelope code, and found it to be bulky and nasty. Therefor, I am wanting to rework some of the syntax relating specifically to "inheritance" idioms, as follows (with "..." indicating "no change"):
Code: [Select]
PSUEDO-JAVA/C#        | | ANTELOPE (new syntax) | | ANTELOPE (old syntax)   
========================================================================
class Fooer {         | | struct Fooer {        | | ...                     
  void Foo() {        <1>   func Foo() {        <1> func(this) Foo = func(this) {
    Print("Foo ");    | |     Print("Foo");     | |   ...                   
  }                   | |   }                   | | }                       
}                     | | }                     | | ...                     
                      | |                       | | ...                     
class FooID : Fooer { <2> struct FooID {        <2> ...                     
  int id;             | |   int id;             | | ...                     
                      | |                       | | ...                     
                      <3>   Fooer {             <3> Fooer = Fooer {         
  void Foo() {        <4>     func Foo() {      <4>   func(this) {         
    Print("Foo ");    | |       Print("Foo ");  | |     ...                 
    Print(id);        | |       Print(id);      | |     ...                 
  }                   | |     }                 | |   }                     
                      | |   }                   | | }                       
}                     | | }                     | | ...                     
1) I'd previously given the "func(this..." syntax for declaring function-pointers "as methods" (i.e. "f.Foo(f)" shortens to "f.Foo()"), which allowed "virtual" (late-bound) methods to be "simulated". However, letting these be declared directly as "internal methods" looks much cleaner, matches the common OOP idiom/paradigm, and still gives control over where they are stored in the struct.

2) Antelope rejects traditional "inheritance" in favor of composition. Instead, anonymous members provide transparent access to their internals, as if they were "inherited" directly into the containing struct. Thus, the "inherited" Fooer appears in the body of the struct rather than at the top (as in 3).

3) Since anonymous members exist soley to "simulate inheritance", it is just redundant to have to repeat the anonymous type twice when a more compact form will do. This form is suggestive of embedding an "anonymous class" within a struct, which is exactly the intent (as in 6).

4) Since "inheritance" is simulated by composition, the new "Foo" function must be embedded directly in the anonymous Fooer. However, the only safe option is to use a "Fooer" method (since a "FooID" method could have misalignment issues) declared anonymously (to hide it from "Fooers" outside of FooID) and then type-cast to "FooID" within the method. This new form takes a non-method function definition for overriding (replacing) "inherited" methods, which then does these things automatically (using offsets if necessary). These can be given anywhere in the initialization list, and are skipped if omitted.
Code: [Select]
PSUEDO-JAVA/C#        | | ANTELOPE (new syntax) | | ANTELOPE (old syntax)
======================================================================
Fooer foo = Fooer();  <5> Fooer foo = Fooer{};  <5> ...                   
FooID fid = FooID(5); | | FooID fid = FooID{5}; | | ...                   
                      | |                       | | ...                   
Fooer moo = Fooer() { <6> Fooer moo = Fooer {   <6> ...                   
  void Foo() {        | |   func Foo() {        | | func(*Fooer f) {     
    super.Foo();      | |     Fooer.Foo(this);  | |   Fooer.Foo(f);       
    Print(" Moo!");   | |     Print(" Moo!");   | |   ...                 
  }                   | |   }                   | | }                     
}                     | | }                     | | ...                   
5) Initialization values are given directly "{...}" rather than via a constructor "(...)". The first variable declaration can be simplified to either "Fooer foo = {5};" or  "foo := Fooer{5};" for conciseness.

6) "Inner" methods are normally skipped in initialization, but new versions can be defined for them (in any order) (as in 4). This very closely resembles an "abstract class" declaration in Java, which likewise creates an instance with "new" methods (only the Antelope version does not require a "new version" of the struct: this is merely a regular initialization which passes an anonymous function to the function-pointer "Foo").
Code: [Select]
PSUEDO-JAVA/C#               | | ANTELOPE (nothing new here)
=============================================================
void FooFooer(Foo f) {       <7> func FooFooer(*Foo f) {     
  f.Foo();                   | |   f.Foo();                 
}                            | | }                           
                             | |                             
FooFooer(foo); // "Foo"      <8> FooFooer(foo); // "Foo"     
FooFooer(fid); // "Foo 5"    | | FooFooer(fid); // "Foo 5"   
FooFooer(moo); // "Foo Moo!" | | FooFooer(moo); // "Foo Moo!"
7) In Java/C#, pointers ("references") are automatic. In Antelope you have to use them specifically (and you should, because they are more efficient).

8) The compiler will automatically pass the address of these variables so as to match the type. Otherwise, this code is exactly the same. Alternatively, FooFooer could have been written as a method ("func Fooer.FooFooer()").

NOTE: Although there were short forms (e.g. "Foo :=" rather than "func(this) Foo =" (at 1), or "Foo := Fooer{}" or "Fooer Foo = {}" (at 5)), I used the fully expanded forms for a more complete comparison of the fully written out forms.

9
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: July 29, 2012, 05:08:35 pm »
Cool. I like the preprocessor directives especially since I'm starting to understand their usefulness (remember, I have mostly confined myself to C# where they are largely unnecessary). Do you plan to add #pragma, or would that not be needed?
No pragma, because I intend on having my compiler be "the" compiler for Antelope (like how java uses "javac" as the core compiler, but various IDE's can be built off of it). Instead, by exposing the guts of the compiler as an API, other tools can "intercept" code between phases and do whatever else they want (including intercepting a "#pragma" anyway).

Also, I will have the compiler strictly allow each file to be used only once, with context of usage not playing any significant role (other than referring to relative file-locations). However, I'd like to allow URL's in #includes too! :) I will probably let the compiler take an argument to specify default url paths as well as default file paths, so that other tools (IDE's) can provide environment control separately from the source code.

On a side note, "projects" could be managed simply enough by creating one file with an "#include" for everything else; and since this can be done with "#define" as well, perhaps it might be worth providing a "#nodef" or "#ignore" directive to veto specific things beforehand.

10
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: July 29, 2012, 11:48:09 am »
*Bump* - I have made changes to the Overview, with the following changes:

 - Tuples can now have empty items (e.g. use "(,x,)" instead of "(_,x,_)").
 - Added a detailed section just for namespaces.
 - Changed the preprocessor directives to be more traditional
 - Elaborated on how default values work (not the same as in other languages!)
 - Gave example of how anonymous entities in a namespaces take on the namespace name.

11
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: July 27, 2012, 04:38:48 am »
Does this mean we have to use C++ style method declaration? I.e.
   struct Foo { ... }
   func Foo::Bar() : byte { ... }

Yes, this has always been the case (so that you can add new methods you as needed, without affecting the "definition" of a struct). Allowing them inside of structs was something I had added for familiarity with Java/C# as well. However, since you can embed function pointers inside structs (and make them act as virtual methods as well), I thought it would minimize confusion (and simplify the language) to just stick with one convention:

Code: [Select]
struct Foo {
   func(*Foo,...) fp; // embedded function pointer
   func(this,...) mp; // just like fp, but counts as a "method"
}

func f(*Foo,...) { ... } // function (not embedded in Foo)
func Foo.m(...) { ... } // method (same as f, but is a method)

Foo foo, foo2;
foo.fp(foo,...); // foo EXPLICITLY passed as 1st arg
foo.fp(foo2,...); // same deal (can pass ANY Foo)
foo.mp(...); // "method" = foo IMPLICITLY passed as 1st arg ("this")
f(foo,...); // same deal as fp, but not a func-pointer
foo.m(...); // same deal as mp, but not a func-pointer

// They all STORED as func(*Foo,...) though;
// and since fp and mp are pointers, you can do this:
foo.fp = foo.mp = f; foo.fp = foo.mp = m;

I honestly prefer the "inside" form myself; but it's not hard to get used to, and you have the following benefits:
 * The struct JUST contains what it actually consists of (e.g. it "is" its "constructor"; the end).
 * This is simpler than in C++: You use "." insteaf of "::", and only declare it once (no "headers").
If enough people are not appeased by these benefits (and my other reasoning), then I might reconsider. I do want to make a language that people like as well.

12
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: July 26, 2012, 05:14:22 pm »
I have made all recently mentioned changes to the grammar!

 - Templated types (e.g. Foo<T:Printer> { ... } )
 - Extra "this" type for cofuncs.
 - No more embedding of funcs or cofuncs within structs or cofuncs (enforces clarity)
 - You can now be more liberal with the "dot" access (A.func(...), B.(*type), etc.).

Have a look :)

13
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: July 23, 2012, 04:56:53 pm »
*Bump* - Here are some new changes:

I have reverted the "cofunc" syntax back so that they cannot be embedded within structs, but they can hold data like structs. This is slightly less flexible (only because bizarre"siamese" cofuncs are no longer possible), but is more intuitive:

Code: [Select]
// Old setup:
struct Counter { int n; cofunc( ):int { n++; return n; } }
c := Counter{5};
c(); c(); c(); // 6, 7, 8

// New setup:
cofunc Counter { int n; } ( ):int { n++; return n; }
c := Counter{5};
c(); c(); c(); // 6, 7, 8

I have also decided to go with the "Templating" form of parametric types (though I still need to choose an unambiguous syntax for multiple parameters), but I will have templated things share the same functions when they are the same. This means that you can still use POINTERS to keep things generic for all types :)

Code: [Select]
struct Link<T> { T value; *Link<T> next = null; }
struct List<T> { *Link<T> head = null, tail = null; length = 0; }

func Tree<T>.Size<T>():int { return length; }

func Tree<T>.add<T>(T val) {
   l := new Link<T>{val};
   if(head == null) { head = tail = l; }
   else { tail = tail.next = l; }
   length++;
}

List<int> list; // an int list
list.add(1); ist.add(1); ist.add(3);
If it were "*T value" rather than "T value" in Link, then there'd only one version of everything, since pointers are the same size. Also, you can be specific, such that a "func List<int>.Foo()" would take ONLY int-lists (which is why the extra "<T>" is needed in "List<T>.add<T>").

I have also added the option of an extra "this" for cofuncs, removed the empty parenthesis from no-argument lambdas, allowed lambdas to have full {bodies}, ... and in the process, I have discovered that passing a function-pointer is MUCH more efficient than using a cofunc as an "iterator". Here is an example of all (riding off that "List" code):

Code: [Select]
List<int> list;

cofunc List<T>.Iterator<T>():*T {
   for(l := head; l != null; l = l.next) yield l.value;
}

func List<T>.Each<T>(func(*T) process) {
   for(l := head; l != null; l = l.next) process(l.value);
}

// Cofunc iteration (uses two loops!)
for(int i: list.Iterator{}) { ...do stuff with i... }
// Func-pointer iteration (much more efficient)
list.Each(i => {...do stuff with i...});

The only thing is that you cannot encapsulate everything into a function; though perhaps I can allow direct access to local variables, but then disallow such funcs from "escaping" the embedded function by restricting their use (e.g. cannot be assigned to external variables or passed into external routines).

14
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: July 22, 2012, 06:18:48 pm »
1) you could just make Printer an abstract class with a default method
2) Also, are you sure Foo and Foo<T> would have the same methods?
3) Foo<T>'s type would probably be something like IEnumerable<T>.

1) There are no classes in Antelope, and no "abstract" mechanism. There are just structs, interfaces, and methods, which are all separate features (to be very technical, nothing "has" methods, but there may "be" methods "for" something; they are given additionally). However, you can use embedded function-pointers to simulate some of that behavior:

Code: [Select]
struct abstract {
   int x, y;
   meth := func(this, int a, int b) { x=a; y=b; };
}

a1 = abstract{1, 2}; // uses defualt method
a2 = abstract{1, 2, someOtherMethod};

2) I am actually leaning toward the "templating" setup, so that if there is a "Foo<T>", then it will be incorrect to have a plain "Foo". The compiler will generate different methods for each type used, but will share methods which would be exactly the same. This means that you can use a pointer to keep everything generic:

Code: [Select]
struct Foo<T> { *T value; } // always the same size
func Foo<T>.Bar<T>() { ... } // always the same everything
func Foo<int>.Bar() { ... } // THIS one is specific to ints.

c := Foo<char>{'5'};
b := Foo<bool>{true};
i := Foo<int>{123};

c.Bar(); // inferred as c.Bar<char>, or Foo<T>.Bar<T> where T = char
b.Bar(); // inferred as b.Bar<bool>, or Foo<T>.Bar<T> where T = bool
i.Bar(); // inferred as i.Bar<int>, or the separate Foo<int>.Bar

// --- Another just to clarify: ---
func A<T>(T arg) { ... } // Different per type-size (and per differing operations on T)
func B<T>(*T arg) { ... } // Same per type, so long as nothing type-specific happens

3) ...Nothing would be quite like "IEnumerable<T>"; though yes, they share the same kind of generic idea. On a side note though, Antelope abandons the concept of "iterators" as they are in Java, C++. Instead, you'd iterate over things directly within a cofunc (which, by the way, was inspired by the "enumerators" or C# and the coroutines of Lua):

Code: [Select]
struct Tree<T> { T value; *Tree<T> left, right; }

cofunc Tree<T>.InOrder<T>():T {
   if(left != null)
      for(int i : new left.InOrder{}) { yield i; }
   yield value;
   if(right != null)
      for(int i : new right.InOrder{}) { yield i; }
}

Tree<int> tree; // Tree of int values
...
for(int i : tree.InOrder{}) { Print(i); }

EDIT: I actually found that using function-pointers as "visitors" is a MUCH more efficient pattern:

Code: [Select]
struct Tree<T> { T value; *Tree<T> left, right; }

func Tree<T>.InOrder<T>(func(T) process) {
   if(left != null) { left.InOrder(process); }
   process(value);
   if(right != null) { right.InOrder(process); }
}

Tree<int> tree;
...
tree.InOrder(Print);
// Or: tree.InOrder(i => somethingElse(i));
(SEE MY NEXT POST)

15
TI Z80 / Re: Antelope (formerly "OPIA") - A Polymorphic z80 language
« on: July 20, 2012, 04:56:54 pm »
If it's my understanding, then "struct Foo<T:Printer> { []T data; }" and "struct Foo<Printer> { []Printer data; }" are basically the same. ... Not every Print() method would be the same for every Printer.

Sort of... The intention is that, where the Print() method is the same, you would only have to store one of them. For example, a Foo<Bar> would be a "Foo" containing "Bar" values, and therefor each using the same "Bar.Print" method. If you want a structure of mixed "Printers", then you would just use the Interface directly:

Code: [Select]
interface Printer { Print(); }

// Using "mixed" Printers ("<>" is unnecessary)
struct Mixed { []Printer data; }
Mixed m = {{someBar, someCar, someDar}};
//m is: {{Printer{someBar,Bar.Print}, Printer{someCar,Car.Print}, Printer{someDar,Dar.Print}}}
m.data[i].Print(); // Calls the "Print" of data[i] on the Printer-object of data[i]

// Using only same-typed Printers:
struct Same<T:Printer> { []T data; }
Same<Bar> s = {{bar1, bar2, bar3}};
// s is: {{bar1,bar2,bar3}, Bar.Print}
s.data[i].Print(); // Calls the single embedded Bar.Print on data[i]

The idea with this style is that the parameterization (<T:Printer>) just acts as a way to base a structure on some type which qualifies as some interface, but to store it as is and use the relevant method. The method must still be stored (since the structure does not know which type it ever since, being a generic structure based on a pointer to "something"), but is stored separately for efficiency. However, if you want a bunch of Printers (not things based on Printers, but actual Printers), then just use the Printer type directly!

The conundrum again is as follows:

This "generic" style has the "win" of providing a more efficient "generic" version. You could even have a plain "Foo" as a function argument, and pass it either a "Foo<Bar>" or a "Foo<int>", and it will work all the same using the same function, because the relevant method is embedded. Besides, a "templated" form is a convenience which CAN be coded by hand. That is, if you want a structure of int values, then go ahead and make one.

HOWEVER, without a "template"-based setup, it is impossible to make a standard library for such. For example, someone could make a very nice tree structure based on a "generic" type, or even just on an int; but if you wanted one made JUST for strings, you would have to code it all over again just for strings. So a "templated" setup has a win there; but would then not allow for the generic behavior I just described, result in multiple vesions of everything (bloat), and it would be impossible to have a runtime (precompiled) library of such, since every possible type cannot be anticipated that way.

C# actually uses something between the two forms (it uses one definition of everything, but the "reifies" them at runtime ... something like that) ... I am going to go read more on how C# does it and see if I get any ideas; though Antelope does not use a virtual environment and JIT is not applicable.

By the way, that "<A B>" syntax might not be a bad idea; though it would be the only thing in the language which does not use a separator (comma or semicolon) between arguments...

Pages: [1] 2 3 ... 6