www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - [OT] What should be in a programming language?

reply Jason House <jason.james.house gmail.com> writes:
I've been thinking lately about what my ideal programming language would look
like and what I might write if I tried to create one. Below is a short list of
big things I'd do. What do you think of my ideas? What would you do?

I'd look to D and Scala for inspiration, but want to allow better reasoning
about code.

I'd use D's transitive const and use transitive pure instead of transitive
immutable.

Functions can't effect glabal variables unless marked as such. I'll have to
learn what a monad is so that some side effects can be hidden? 

By default, a function can not keep its variables past function scope. If a
parameter or local variable is to remain for longer it must be marked as such
in its type.

Similarly, all function arguments will default to (transitive) const. Any
mutable arguments must be marked as such. I'm unsure if ref arguments should
come in a final and non-final form. One nice side effect of this is that value
types vs. reference types don't require special consideration.  

I'd probably do away with out arguments in favor of return tuples.

All types are not nullable by default. T and T? seem reasonable enough. I'd
probably twist that a bit and use a beefed up variable declaration similar to
scala. "pure? x: T = ..." would be a nullable immutable variable of type T.

Compile time capabilities would exceed D's. I'm thinking of a simple marker to

be a manifest constant. An array with some compile time values would be easy
[foo(7), #bar(8)]. This also means #switch and #foreach would exist (among
other things). Generics with compile time arguments become templates.

I would have a type type, usable for both compile time and run time reflection.

I've probably forgotten a number of basic things, but that should be enough for
now.
Oct 23 2009
next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 23 Oct 2009 16:42:31 +0400, Jason House  
<jason.james.house gmail.com> wrote:

 I've been thinking lately about what my ideal programming language would  
 look like and what I might write if I tried to create one. Below is a  
 short list of big things I'd do. What do you think of my ideas? What  
 would you do?

 I'd look to D and Scala for inspiration, but want to allow better  
 reasoning about code.

 I'd use D's transitive const and use transitive pure instead of  
 transitive immutable.

 Functions can't effect glabal variables unless marked as such. I'll have  
 to learn what a monad is so that some side effects can be hidden?
Agreed.
 By default, a function can not keep its variables past function scope.  
 If a parameter or local variable is to remain for longer it must be  
 marked as such in its type.
A good idea probably.
 Similarly, all function arguments will default to (transitive) const.  
 Any mutable arguments must be marked as such. I'm unsure if ref  
 arguments should come in a final and non-final form. One nice side  
 effect of this is that value types vs. reference types don't require  
 special consideration.

 I'd probably do away with out arguments in favor of return tuples.
Yes, it's reasonable.
 All types are not nullable by default. T and T? seem reasonable enough.  
 I'd probably twist that a bit and use a beefed up variable declaration  
 similar to scala. "pure? x: T = ..." would be a nullable immutable  
 variable of type T.
Yup!
 Compile time capabilities would exceed D's. I'm thinking of a simple  

 if. #pure would be a manifest constant. An array with some compile time  
 values would be easy [foo(7), #bar(8)]. This also means #switch and  
 #foreach would exist (among other things). Generics with compile time  
 arguments become templates.
Nice idea that solves some of the D issues quite gracefully.
 I would have a type type, usable for both compile time and run time  
 reflection.
I'd separate that into built-in "type" and library "Type" types.
 I've probably forgotten a number of basic things, but that should be  
 enough for now.
I believe templates are better be written in imperative style. That's why a built-in "type" type is needed. Great list. I believe with upcoming finalization of D2 some of us are already looking into future and D3 so keep thinking. While you get your inspiration from D, D could also adopt some of your suggestions.
Oct 23 2009
next sibling parent Jason House <jason.james.house gmail.com> writes:
Denis Koroskin Wrote:

 On Fri, 23 Oct 2009 16:42:31 +0400, Jason House  
 <jason.james.house gmail.com> wrote:
 [snip]  
 I would have a type type, usable for both compile time and run time  
 reflection.
I'd separate that into built-in "type" and library "Type" types.
I don't really understand how you're envisioning use of types. Can you elaborate a bit more? I'd normally assume the library Type class would be the same as used by the compiler. That would seem to keep things clean/complete, but there's no reason someone couldn't make an enhanced wrapper which would also be usable at compile time
 I've probably forgotten a number of basic things, but that should be  
 enough for now.
I believe templates are better be written in imperative style. That's why a built-in "type" type is needed.
Absolutely! Do you have any inspirational examples?
 Great list. 
Thanks
 I believe with upcoming finalization of D2 some of us are  
 already looking into future and D3 so keep thinking. While you get your  
 inspiration from D, D could also adopt some of your suggestions.
Maybe. Somehow I think most of my list is too extreme. D has even moved away from some of the stuff in my list. (for example, in used to be scope const)
Oct 23 2009
prev sibling parent BCS <none anon.com> writes:
Hello Denis,


 I believe templates are better be written in imperative style. That's
 why  a built-in "type" type is needed.
The problem there is that real template (that, at compile time, generate a difference instance per type) are fundamentally declarative rather than imperative. What might work best is having template be declarative but provide powerful enough compile time imperative constructs that make functional programing (e.g. C++ style meta programming) more or less pointless. The thought is that rather than mix compile time and runtime imperative construct the template can /declare/ "there is a ___" but can access pure compile time constructs to do whatever processing/computation is needed.
Oct 27 2009
prev sibling next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 23/10/2009 14:42, Jason House wrote:
 I've been thinking lately about what my ideal programming language
 would look like and what I might write if I tried to create one.
 Below is a short list of big things I'd do. What do you think of my
 ideas? What would you do?

 I'd look to D and Scala for inspiration, but want to allow better
 reasoning about code.

 I'd use D's transitive const and use transitive pure instead of
 transitive immutable.

 Functions can't effect glabal variables unless marked as such. I'll
 have to learn what a monad is so that some side effects can be
 hidden?

 By default, a function can not keep its variables past function
 scope. If a parameter or local variable is to remain for longer it
 must be marked as such in its type.

 Similarly, all function arguments will default to (transitive) const.
 Any mutable arguments must be marked as such. I'm unsure if ref
 arguments should come in a final and non-final form. One nice side
 effect of this is that value types vs. reference types don't require
 special consideration.

 I'd probably do away with out arguments in favor of return tuples.

 All types are not nullable by default. T and T? seem reasonable
 enough. I'd probably twist that a bit and use a beefed up variable
 declaration similar to scala. "pure? x: T = ..." would be a nullable
 immutable variable of type T.

 Compile time capabilities would exceed D's. I'm thinking of a simple

 static if. #pure would be a manifest constant. An array with some
 compile time values would be easy [foo(7), #bar(8)]. This also means
 #switch and #foreach would exist (among other things). Generics with
 compile time arguments become templates.

 I would have a type type, usable for both compile time and run time
 reflection.

 I've probably forgotten a number of basic things, but that should be
 enough for now.
my list would be similar: non-nullable references by default, concurrency semantics (I liked Bartosz' design), transitive const and everything is const by default, immutable as part of the concurrency ownership design, functions would be defined as in ML: they take one tuple argument and return one tuple argument i don't like the #if, etc idea and would prefer a proper AST macro system with proper hygiene I'd remove static from the language completely and instead would have metaclasses. other OOP changes - better separation between sub-typing and sub-classing: type interfaceA extends interfaceB, interfaceC {} implementation implA extends implB, implC implements interfaceA, interfaceD{} auto obj = interfaceA.new(); // returns an implementation, e.g. implA ionstead of the classic: class A {} class B: A {} we'll have: type A {} type B extends A {} implementation A implements A{} implementation B extends implementation A implements B {} everything is an object and no special treatment for specific types
Oct 23 2009
parent reply Jason House <jason.james.house gmail.com> writes:
Yigal Chripun Wrote:

 On 23/10/2009 14:42, Jason House wrote:
 I've been thinking lately about what my ideal programming language
 would look like and what I might write if I tried to create one.
 Below is a short list of big things I'd do. What do you think of my
 ideas? What would you do?

 I'd look to D and Scala for inspiration, but want to allow better
 reasoning about code.

 I'd use D's transitive const and use transitive pure instead of
 transitive immutable.

 Functions can't effect glabal variables unless marked as such. I'll
 have to learn what a monad is so that some side effects can be
 hidden?

 By default, a function can not keep its variables past function
 scope. If a parameter or local variable is to remain for longer it
 must be marked as such in its type.

 Similarly, all function arguments will default to (transitive) const.
 Any mutable arguments must be marked as such. I'm unsure if ref
 arguments should come in a final and non-final form. One nice side
 effect of this is that value types vs. reference types don't require
 special consideration.

 I'd probably do away with out arguments in favor of return tuples.

 All types are not nullable by default. T and T? seem reasonable
 enough. I'd probably twist that a bit and use a beefed up variable
 declaration similar to scala. "pure? x: T = ..." would be a nullable
 immutable variable of type T.

 Compile time capabilities would exceed D's. I'm thinking of a simple

 static if. #pure would be a manifest constant. An array with some
 compile time values would be easy [foo(7), #bar(8)]. This also means
 #switch and #foreach would exist (among other things). Generics with
 compile time arguments become templates.

 I would have a type type, usable for both compile time and run time
 reflection.

 I've probably forgotten a number of basic things, but that should be
 enough for now.
my list would be similar: non-nullable references by default, concurrency semantics (I liked Bartosz' design),
That's an important one that I forgot to list: an ownership scheme similar to Bartosz's (I'd favor a slight reduction in annotation, but the same basic thing)
 transitive const and everything is const by default, 
 immutable as part of the concurrency ownership design,
 functions would be defined as in ML: they take one tuple argument and 
 return one tuple argument
I like scala's concept of multiple input tuples, and the freedom to use () or {} when specifying a tuple. I might be slightly stricter with how each tuple is used such that the library writer says which form is allowed. This has the awesome side effects of making libraries look like part of the language.
 
 i don't like the #if, etc idea and would prefer a proper AST macro 
 system with proper hygiene
The AST macro stuff predates my participation on this list. Have any good links explaining how it works? I've been thinking of allowing expressions, statements (and friends) as object types and then allowing mixing in of those objects... #var declareX = Statement ("int x=3"); #mixin declareX; ... or something along those lines...
 
 I'd remove static from the language completely and instead would have 
 metaclasses.
I agree with no statics. What are metaclasses? I like how scala defines singletons for statics (object in scala type declaration)
 other OOP changes - better separation between sub-typing and sub-classing:
 
 type interfaceA extends interfaceB, interfaceC {}
 implementation implA extends implB, implC implements interfaceA, 
 interfaceD{}
 
 auto obj = interfaceA.new(); // returns an implementation, e.g. implA
 
 ionstead of the classic:
 class A {}
 class B: A {}
 
 we'll have:
 type A {}
 type B extends A {}
 implementation A implements A{}
 implementation B extends implementation A implements B {}
There seems to be redundency here. Why would you/others want that? Super verbose code makes it tough to attract users.
 everything is an object and no special treatment for specific types
I don't think "everything is an object" works under the hood, but I do like making that transparent. A lot of the items allow transparent use of reference and value types (scope by default unless explicitly marked/allocated, and transitive const by default unless passed by ref)
Oct 23 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 23/10/2009 19:50, Jason House wrote:
 Yigal Chripun Wrote:
<snip>
 transitive const and everything is const by default, immutable as
 part of the concurrency ownership design, functions would be
 defined as in ML: they take one tuple argument and return one tuple
 argument
I like scala's concept of multiple input tuples, and the freedom to use () or {} when specifying a tuple. I might be slightly stricter with how each tuple is used such that the library writer says which form is allowed. This has the awesome side effects of making libraries look like part of the language.
i don't know about this specific feature of Scala. in D you can pass a (type) tuple to a function and it is auto flattened to the arg list. i.e. void foo(int, int); Tuple!(int, int) a = ...; foo(a); in ML, the arg list *is* a tuple so there's no need for an auto-flatten. you can always nest tuples so it's trivial to have multiple tuples as arguments. example (not tested): fun foo (a, (b, c)) = a*b + a*c let bar = foo(3, (4, 5)) foo's signature would be: ('a, ('a, 'a)) -> ('a)
 i don't like the #if, etc idea and would prefer a proper AST macro
 system with proper hygiene
The AST macro stuff predates my participation on this list. Have any good links explaining how it works? I've been thinking of allowing expressions, statements (and friends) as object types and then allowing mixing in of those objects... #var declareX = Statement ("int x=3"); #mixin declareX; ... or something along those lines...
http://en.wikipedia.org/wiki/Nemerle http://en.wikipedia.org/wiki/Hygienic_macro here's a quick summary of how it should work/look like: macros are written in the same language, no #if, no static if. you write regular looking functions in plain code. those macros are compiled separately into loadable libs that you can specify for the compiler to load. the language has syntax to [de]compose AST.
 I'd remove static from the language completely and instead would
 have metaclasses.
I agree with no statics. What are metaclasses? I like how scala defines singletons for statics (object in scala type declaration)
there are different models for this and this also relates to the macro system above. here's one model (used in smalltalk) class Foo { int a; void foo(); } the compiler will generate for Foo a singleton of the type 'Foo which contains all the information about Foo. for example, it'll contain the list of functions for Foo instances. this is the same as in D - in D we have Typeinfo structs that contain vtables. class Bar { int a; static void foo(); } in compiled languages (c++/d) this is done statically (foo is a global function in the assembly) in smalltalk the previous mechanism is [re]used: we have class Bar which defines it's instances we have class 'Bar that defines Bar we have class ''Bar that defines 'Bar we have class Class that defines ''Bar total of 5 levels which are required to have class shared functions/state
 other OOP changes - better separation between sub-typing and
 sub-classing:

 type interfaceA extends interfaceB, interfaceC {} implementation
 implA extends implB, implC implements interfaceA, interfaceD{}

 auto obj = interfaceA.new(); // returns an implementation, e.g.
 implA

 ionstead of the classic: class A {} class B: A {}

 we'll have: type A {} type B extends A {} implementation A
 implements A{} implementation B extends implementation A implements
 B {}
There seems to be redundency here. Why would you/others want that? Super verbose code makes it tough to attract users.
I was trying to convey semantics here rather than syntax. deriving a class from a base class is a poor way to design code and I want to separate two orthogonal issues - a. subtyping and polymorphism b. reuse of code/ implementation
 everything is an object and no special treatment for specific
 types
I don't think "everything is an object" works under the hood, but I do like making that transparent. A lot of the items allow transparent use of reference and value types (scope by default unless explicitly marked/allocated, and transitive const by default unless passed by ref)
c style built in types expose an implementation detail that should be encapsulated and hidden under the hood as you say. there should be no syntactic difference between an int and a user defined type. for example I should be able to do: struct foo : int {}
Oct 23 2009
parent reply Jason House <jason.james.house gmail.com> writes:
Yigal Chripun Wrote:

 On 23/10/2009 19:50, Jason House wrote:
 Yigal Chripun Wrote:
<snip>
 transitive const and everything is const by default, immutable as
 part of the concurrency ownership design, functions would be
 defined as in ML: they take one tuple argument and return one tuple
 argument
I like scala's concept of multiple input tuples, and the freedom to use () or {} when specifying a tuple. I might be slightly stricter with how each tuple is used such that the library writer says which form is allowed. This has the awesome side effects of making libraries look like part of the language.
i don't know about this specific feature of Scala.
My web search and some PDF's didn't turn up a handy example. You can do things in scala like define your own foreach loop. If foreach had the form form foreach(x){y} then x would be one set of arguments and y would be another set. It makes for pretty use of library functions. They look built in!
 in D you can pass a 
 (type) tuple to a function and it is auto flattened to the arg list.
 i.e.
 void foo(int, int);
 Tuple!(int, int) a = ...;
 foo(a);
 in ML, the arg list *is* a tuple so there's no need for an auto-flatten. 
 you can always nest tuples so it's trivial to have multiple tuples as 
 arguments.
 
 example (not tested):
 
 fun foo (a, (b, c)) = a*b + a*c
 let bar = foo(3, (4, 5))
 
 foo's signature would be: ('a, ('a, 'a)) -> ('a)
Sounds reasonable
 i don't like the #if, etc idea and would prefer a proper AST macro
 system with proper hygiene
The AST macro stuff predates my participation on this list. Have any good links explaining how it works? I've been thinking of allowing expressions, statements (and friends) as object types and then allowing mixing in of those objects... #var declareX = Statement ("int x=3"); #mixin declareX; ... or something along those lines...
http://en.wikipedia.org/wiki/Nemerle http://en.wikipedia.org/wiki/Hygienic_macro here's a quick summary of how it should work/look like: macros are written in the same language, no #if, no static if. you write regular looking functions in plain code. those macros are compiled separately into loadable libs that you can specify for the compiler to load. the language has syntax to [de]compose AST.
I looked over the links (quickly). I must admit I don't get it yet. It takes me a while to digest lisp fragments... Can you give a D-ish example of what it'd look like?
 I'd remove static from the language completely and instead would
 have metaclasses.
I agree with no statics. What are metaclasses? I like how scala defines singletons for statics (object in scala type declaration)
there are different models for this and this also relates to the macro system above. here's one model (used in smalltalk) class Foo { int a; void foo(); } the compiler will generate for Foo a singleton of the type 'Foo which contains all the information about Foo. for example, it'll contain the list of functions for Foo instances. this is the same as in D - in D we have Typeinfo structs that contain vtables. class Bar { int a; static void foo(); } in compiled languages (c++/d) this is done statically (foo is a global function in the assembly) in smalltalk the previous mechanism is [re]used: we have class Bar which defines it's instances we have class 'Bar that defines Bar we have class ''Bar that defines 'Bar we have class Class that defines ''Bar total of 5 levels which are required to have class shared functions/state
That seems strange. I'm also missing something important :(
 other OOP changes - better separation between sub-typing and
 sub-classing:

 type interfaceA extends interfaceB, interfaceC {} implementation
 implA extends implB, implC implements interfaceA, interfaceD{}

 auto obj = interfaceA.new(); // returns an implementation, e.g.
 implA

 ionstead of the classic: class A {} class B: A {}

 we'll have: type A {} type B extends A {} implementation A
 implements A{} implementation B extends implementation A implements
 B {}
There seems to be redundency here. Why would you/others want that? Super verbose code makes it tough to attract users.
I was trying to convey semantics here rather than syntax. deriving a class from a base class is a poor way to design code and I want to separate two orthogonal issues - a. subtyping and polymorphism b. reuse of code/ implementation
That's a good goal. What should it look like in code?
 everything is an object and no special treatment for specific
 types
I don't think "everything is an object" works under the hood, but I do like making that transparent. A lot of the items allow transparent use of reference and value types (scope by default unless explicitly marked/allocated, and transitive const by default unless passed by ref)
c style built in types expose an implementation detail that should be encapsulated and hidden under the hood as you say. there should be no syntactic difference between an int and a user defined type. for example I should be able to do: struct foo : int {}
I agree
Oct 24 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 25/10/2009 06:26, Jason House wrote:

 My web search and some PDF's didn't turn up a handy example. You can
 do things in scala like define your own foreach loop. If foreach had
 the form form foreach(x){y} then x would be one set of arguments and
 y would be another set. It makes for pretty use of library functions.
 They look built in!
isn't that similar in concept to code blocks?
 i don't like the #if, etc idea and would prefer a proper AST
 macro system with proper hygiene
The AST macro stuff predates my participation on this list. Have any good links explaining how it works? I've been thinking of allowing expressions, statements (and friends) as object types and then allowing mixing in of those objects... #var declareX = Statement ("int x=3"); #mixin declareX; ... or something along those lines...
http://en.wikipedia.org/wiki/Nemerle http://en.wikipedia.org/wiki/Hygienic_macro here's a quick summary of how it should work/look like: macros are written in the same language, no #if, no static if. you write regular looking functions in plain code. those macros are compiled separately into loadable libs that you can specify for the compiler to load. the language has syntax to [de]compose AST.
I looked over the links (quickly). I must admit I don't get it yet. It takes me a while to digest lisp fragments... Can you give a D-ish example of what it'd look like?
here's a Nemerle example: macro PrintStage() { System.Console.WriteLine("This is executed during compilation"); <[ System.Console.WriteLine("This is executed at run time") ]> } the first WriteLine is executed during compilation, and the macro returns the AST for the second WriteLine which will be executed at run time when this macro is called. think of it like: // hypothetical D syntax macro print() { Stdout("Hello compile time world").newline; return q{ Stdout("Hello run time world").newline }; } one important design goal is to clearly separate the stages, so this will go to a separate .d file and will be compiled into a lib. to use this macro you simply specify compiler --load-macro=myMacro sources.d in user code you just use "print();"
 I'd remove static from the language completely and instead
 would have metaclasses.
I agree with no statics. What are metaclasses? I like how scala defines singletons for statics (object in scala type declaration)
there are different models for this and this also relates to the macro system above. here's one model (used in smalltalk) class Foo { int a; void foo(); } the compiler will generate for Foo a singleton of the type 'Foo which contains all the information about Foo. for example, it'll contain the list of functions for Foo instances. this is the same as in D - in D we have Typeinfo structs that contain vtables. class Bar { int a; static void foo(); } in compiled languages (c++/d) this is done statically (foo is a global function in the assembly) in smalltalk the previous mechanism is [re]used: we have class Bar which defines it's instances we have class 'Bar that defines Bar we have class ''Bar that defines 'Bar we have class Class that defines ''Bar total of 5 levels which are required to have class shared functions/state
That seems strange. I'm also missing something important :(
OK, here's an example: class Foo { int a; void bar(); } auto obj = new Foo; obj.a = 42; // obj contains a obj.bar(); // calls 'Foo.vtbl.bar remember that 'Foo is the classinfo singelton for Foo class Foo { static a; static void bar(); } Foo.a = 42; // 'Foo contains a Foo.bar(); // calls ''Foo.vtbl.bar ''Foo is the classinfo singelton for 'Foo we get the following chain ("-->" means instance of) obj --> Foo --> MetaFoo --> MetaClass --> Class compared with C++/D/Java/etc: obj --> Foo --> Class
 other OOP changes - better separation between sub-typing and
 sub-classing:

 type interfaceA extends interfaceB, interfaceC {}
 implementation implA extends implB, implC implements
 interfaceA, interfaceD{}

 auto obj = interfaceA.new(); // returns an implementation,
 e.g. implA

 ionstead of the classic: class A {} class B: A {}

 we'll have: type A {} type B extends A {} implementation A
 implements A{} implementation B extends implementation A
 implements B {}
There seems to be redundency here. Why would you/others want that? Super verbose code makes it tough to attract users.
I was trying to convey semantics here rather than syntax. deriving a class from a base class is a poor way to design code and I want to separate two orthogonal issues - a. subtyping and polymorphism b. reuse of code/ implementation
That's a good goal. What should it look like in code?
that's a good question. I don't know yet.
Oct 25 2009
parent reply Jason House <jason.james.house gmail.com> writes:
Yigal Chripun Wrote:

 On 25/10/2009 06:26, Jason House wrote:
 
 My web search and some PDF's didn't turn up a handy example. You can
 do things in scala like define your own foreach loop. If foreach had
 the form form foreach(x){y} then x would be one set of arguments and
 y would be another set. It makes for pretty use of library functions.
 They look built in!
isn't that similar in concept to code blocks?
I'm not familiar enough with code blocks to say for sure. From what I saw in blogs, they are not. Either way, D can't make things look built in like scala can. IMHO, it's a great programming language feature.
 I looked over the links (quickly). I must admit I don't get it yet.
 It takes me a while to digest lisp fragments... Can you give a D-ish
 example of what it'd look like?
here's a Nemerle example: macro PrintStage() { System.Console.WriteLine("This is executed during compilation"); <[ System.Console.WriteLine("This is executed at run time") ]> } the first WriteLine is executed during compilation, and the macro returns the AST for the second WriteLine which will be executed at run time when this macro is called.
How is that different from a normal function definition that includes some compile-time calls? I agree that compile-time code should look and feel like normal code. It seems you use macro to switch to compile-time by default and runtime when explcitly marked? Having both defaults (compile time or run time) makes sense.
 one important design goal is to clearly separate the stages, so this 
 will go to a separate .d file and will be compiled into a lib.
 to use this macro you simply specify
 compiler --load-macro=myMacro sources.d
 
 in user code you just use "print();"
I disagree with this. The code that uses the macros should declare what it uses.
 
 OK, here's an example:
 
 class Foo {
 int a;
 void bar();
 }
 
 auto obj = new Foo;
 obj.a = 42; // obj contains a
 obj.bar();  // calls 'Foo.vtbl.bar
 
 remember that 'Foo is the classinfo singelton for Foo
 
 class Foo {
 static a;
 static void bar();
 }
 
 Foo.a = 42; // 'Foo contains a
 Foo.bar(); // calls ''Foo.vtbl.bar
 
 ''Foo is the classinfo singelton for 'Foo
 
 we get the following chain ("-->" means instance of)
 obj --> Foo --> MetaFoo --> MetaClass --> Class
 
 compared with C++/D/Java/etc:
 obj --> Foo --> Class
Ok. That makes sense. It can be simplified when statics are removed.
Oct 26 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Jason House Wrote:

 How is that different from a normal function definition that includes some
compile-time calls? I agree that compile-time code should look and feel like
normal code. It seems you use macro to switch to compile-time by default and
runtime when explcitly marked? Having both defaults (compile time or run time)
makes sense.
 
The way it's implemented in Nemerle, a macro is actually a class. the above is not how it works. the code inside a macro is regular run-time code. it is compiled into a lib and loaded by the compiler as a plugin. the code is run at run-time but run-time here means run-time of the compiler since it's a plugin of the compiler. in nemerle (like in FP) the last value in a function is what the function returns. so that macro *returns* an AST representation of what's inside. you can use this operator to de/compose AST.
 
 
 one important design goal is to clearly separate the stages, so this 
 will go to a separate .d file and will be compiled into a lib.
 to use this macro you simply specify
 compiler --load-macro=myMacro sources.d
 
 in user code you just use "print();"
I disagree with this. The code that uses the macros should declare what it uses.
I meant from a syntax POV - calling a macro is the same as calling a function. no template syntax. importing the namespace is still required IIRC.
 
 
 
 OK, here's an example:
 
 class Foo {
 int a;
 void bar();
 }
 
 auto obj = new Foo;
 obj.a = 42; // obj contains a
 obj.bar();  // calls 'Foo.vtbl.bar
 
 remember that 'Foo is the classinfo singelton for Foo
 
 class Foo {
 static a;
 static void bar();
 }
 
 Foo.a = 42; // 'Foo contains a
 Foo.bar(); // calls ''Foo.vtbl.bar
 
 ''Foo is the classinfo singelton for 'Foo
 
 we get the following chain ("-->" means instance of)
 obj --> Foo --> MetaFoo --> MetaClass --> Class
 
 compared with C++/D/Java/etc:
 obj --> Foo --> Class
Ok. That makes sense. It can be simplified when statics are removed.
I don't understand this. How removal of statics simplifies this? I think that having class shared functions/data should still be possible but implemented as above instead of static memory as in c++/D. class Foo { static int value; } this still works as in D but value is a member of the singleton object that represents Foo at runtime instead of stored in static memory. those singletons need to be concurrency friendly unlike the static memory design that is definitely is not. btw, in dynamic languages like smalltalk/ruby those meta classes are mutable so you can for example add methods at run-time. I don't know if this should be allowed in a compiled language.
Oct 26 2009
parent reply Jason House <jason.james.house gmail.com> writes:
Yigal Chripun Wrote:

 Jason House Wrote:
 
 How is that different from a normal function definition that includes some
compile-time calls? I agree that compile-time code should look and feel like
normal code. It seems you use macro to switch to compile-time by default and
runtime when explcitly marked? Having both defaults (compile time or run time)
makes sense.
 
The way it's implemented in Nemerle, a macro is actually a class. the above is not how it works. the code inside a macro is regular run-time code. it is compiled into a lib and loaded by the compiler as a plugin. the code is run at run-time but run-time here means run-time of the compiler since it's a plugin of the compiler. in nemerle (like in FP) the last value in a function is what the function returns. so that macro *returns* an AST representation of what's inside. you can use this operator to de/compose AST.
Your examples in Nemerle or D-ish looked like they are returning strings. I'm still not seeing the magic of AST macros.
 
 OK, here's an example:
 
 class Foo {
 int a;
 void bar();
 }
 
 auto obj = new Foo;
 obj.a = 42; // obj contains a
 obj.bar();  // calls 'Foo.vtbl.bar
 
 remember that 'Foo is the classinfo singelton for Foo
 
 class Foo {
 static a;
 static void bar();
 }
 
 Foo.a = 42; // 'Foo contains a
 Foo.bar(); // calls ''Foo.vtbl.bar
 
 ''Foo is the classinfo singelton for 'Foo
 
 we get the following chain ("-->" means instance of)
 obj --> Foo --> MetaFoo --> MetaClass --> Class
 
 compared with C++/D/Java/etc:
 obj --> Foo --> Class
Ok. That makes sense. It can be simplified when statics are removed.
I don't understand this. How removal of statics simplifies this?
As I understood it 'Foo contains the static data and class info for Foo, and ''Foo contains class info for 'Foo. Without statics, ''Foo is unnecessary. I'm sure I've misinterpreted what you're saying ;)
 I think that having class shared functions/data should still be possible but
implemented as above instead of static memory as in c++/D. 
 class Foo {
 static int value;
 }
 
 this still works as in D but value is a member of the singleton object that
represents Foo at runtime instead of stored in static memory.
The singleton object should be in static memory... I don't really see the distinction since the finer storage details don't affect the programmer.
 
 those singletons need to be concurrency friendly unlike the static memory
design that is definitely is not. 
 
 btw, in dynamic languages like smalltalk/ruby those meta classes are mutable
so you can for example add methods at run-time. I don't know if this should be
allowed in a compiled language. 
Oct 26 2009
parent Yigal Chripun <yigal100 gmail.com> writes:
On 26/10/2009 20:30, Jason House wrote:
 Your examples in Nemerle or D-ish looked like they are returning
 strings. I'm still not seeing the magic of AST macros.
When we want to decompose some large code (or more precisely, its syntax tree), we must bind its smaller parts to variables. Then we can process them recursively or just use them in an arbitrary way to construct the result. We can operate on entire subexpressions by writing $( ... ) or $ID inside the quotation operator <[ ... ]>. This means binding the value of ID or the interior of parenthesized expression to the part of syntax tree described by corresponding quotation. macro for (init, cond, change, body) { <[ $init; def loop () : void { if ($cond) { $body; $change; loop() } else () }; loop () ]> } he above macro defines function for, which is similar to the loop known from C. It can be used like this for (mutable i = 0, i < 10, i++, printf ("%d", i)) /quote the above is taken from the macros_tutorial page of nemerle.org. unfortunately the site is down so I'm using Google's cache instead. there are a few more related topics: Constructs with variable number of elements, hygiene, ...
Oct 26 2009
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Jason House:
 Denis Koroskin:
 I believe templates are better be written in imperative style. That's why  
 a built-in "type" type is needed.
Absolutely! Do you have any inspirational examples?
I have found two my old posts where I have discussed a little about this: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=91015 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=91017 Hope that helps. I'd like to have a type type in D :-) I think it can't replace all current usages of templates, but replaces some of those usages with something simpler to use. Bye, bearophile
Oct 23 2009
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Yigal Chripun:
 there should be no syntactic difference between an int and a user 
 defined type.
 for example I should be able to do:
 struct foo : int {}
With "alias this" D2 is able to do something like that, but it uses a nonstandard syntax... So here things may be improved in D2. Bye, bearophile
Oct 24 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 25/10/2009 08:47, bearophile wrote:
 Yigal Chripun:
 there should be no syntactic difference between an int and a user
 defined type.
 for example I should be able to do:
 struct foo : int {}
With "alias this" D2 is able to do something like that, but it uses a nonstandard syntax... So here things may be improved in D2. Bye, bearophile
This is one aspect that D inherited from C which I really dislike. adding alias this to the mix is adding a sin to a crime (as the Hebrew saying goes). Scala shows how this can be properly designed. D has many (sometimes very powerful) hacks that could and should be replaced with a much better general solution. for instance there's special handling of void return types so it would be easier to work with in generic code. instead of this compiler hack a much simpler solution is to have a unit type and ditch C style void. the bottom type should also exist mainly for completeness and for a few stdlib functions like abort() and exit()
Oct 25 2009
parent bearophile <bearophileHUGS lycos.com> writes:
Yigal Chripun:

 D has many (sometimes very powerful) hacks that could and should be 
 replaced with a much better general solution.
Sometimes fully orthogonal designs aren't the best because they force the programmer to do too much "assembly required". A good language has to offer some handy pre-built/highlevel constructs for the most common operations, that can save lot of work. Sometimes general solutions produce slow programs, or they require a more complex/smarter/slower compiler (I need a new word there, "complexer"? Natural languages too need updates now and then). But I agree that in few parts D can enjoy a redesign. Andrei is doing some work on this, and quite more work can be done. The "alias this" looks like a little hack too me too. A safer or more "theoretically sound" solution may be found. Bye, bearophile
Oct 26 2009
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
Yigal Chripun Wrote:

 for instance there's special handling of void return types so it would 
 be easier to work with in generic code. instead of this compiler hack a 
 much simpler solution is to have a unit type and ditch C style void. the 
 bottom type should also exist mainly for completeness and for a few 
 stdlib functions like abort() and exit()
uint and void return types may be nearly equivalent for x86 architecture, CLI makes strong difference between them.
Oct 26 2009
next sibling parent Jason House <jason.james.house gmail.com> writes:
Kagamin Wrote:

 Yigal Chripun Wrote:
 
 for instance there's special handling of void return types so it would 
 be easier to work with in generic code. instead of this compiler hack a 
 much simpler solution is to have a unit type and ditch C style void. the 
 bottom type should also exist mainly for completeness and for a few 
 stdlib functions like abort() and exit()
uint and void return types may be nearly equivalent for x86 architecture, CLI makes strong difference between them.
Are you two talking about the same thing? uint and unit are quite different from each other. My understanding from scala is that most/all uses of unit are optimized away. I still don't know what unit holds...
Oct 26 2009
prev sibling parent Yigal Chripun <yigal100 gmail.com> writes:
On 26/10/2009 14:47, Kagamin wrote:
 Yigal Chripun Wrote:

 for instance there's special handling of void return types so it would
 be easier to work with in generic code. instead of this compiler hack a
 much simpler solution is to have a unit type and ditch C style void. the
 bottom type should also exist mainly for completeness and for a few
 stdlib functions like abort() and exit()
uint and void return types may be nearly equivalent for x86 architecture, CLI makes strong difference between them.
I have no idea what uint has to do with what I said. in type theory, a unit type contains only one value, and a bottom type contains zero values. the single value of unit can be for example an empty tuple. a function like abort doesn't return anything at all so it's return type is the bottom type. In ML all functions have exactly one tuple argument and one tuple return type. so, for example this c function: void foo(); would have the following signature in ML: unit -> unit if we have: void foo(); void bar(); foo(bar()); is perfectly legal with ML semantics since both functions have the signature: unit -> unit
Oct 26 2009
prev sibling parent Kagamin <spam here.lot> writes:
Jason House Wrote:

 An array with some compile time values would be easy [foo(7), #bar(8)].
Hmm... these array literals work like crazy in dynamically typed languages, but are they the good for statically typed ones?
Oct 26 2009