Open DAddYE opened 10 years ago
An idea would be allow only those statements:
class Fiber
function self::idle end
function self.nready()
return #ready
end
function ready()
if not self.queued then
ready[#ready + 1] = self
self.queued = true
end
self.active = true
end
function foo()
return async do
print "server listening on http://%{host}:%{port}/"
while true do
client = server.accept()
async =>
filter = HTTPFilter(StreamReader(client, nil, 1024))
end
end
end
end
...
Also I do like =>
but we could use just do
(1 thing less to remember) to achieve the same result.
These are 4 ways of doing 4 different things:
class Foo
a_method()
end
function lexical_function()
end
function self.static_method()
end
function self::static_function()
end
end
Foo.a_method() -- Error: attempt to call method 'a_method' (a nil value)
Foo.lexical_function() -- Error: attempt to call method 'lexical_function' (a nil value)
Foo.static_method() -- has implicit `self`
Foo::static_function() -- no implicit self
foo = Foo()
foo.static_method() -- Error: attempt to call method 'static_method' (a nil value)
foo::a_method() -- bad argument #1 to 'a_method' (Foo expected got Nil)
foo.a_method() -- OK
Lexical functions are essentially private to the body of the class unless they're defined with a qualified name (i.e. function foo::bar() end). So replacing method declarations with function
means that Shine will have to drop lexical scopes in class bodies.
There was a long discussion about this during the design of JS Harmony's classes. The eventually decided against them. I prefer to have them, because it gives you a private scope.
Just a note on the context thing... my feeling is that things which are different should look different. Using function
inside a class body for method declarations (assuming I drop lexical class bodies), has completely different semantics to using function
outside of a class, or nested inside another function. Right now, if I say function foo() ... end
I get exactly the same thing regardless of where I do it.
The same goes for function o::foo() ... end
. The fact that o
may be self
inside a class is not special at all; self
is literally just the metatable itself, so there's no difference between the following:
class Foo end
function Foo.bar() end
-- same thing
class Foo
function self.bar()
end
end
regarding init
instead of self
... I feel that init
is actually a useful method name to have available to the user (maybe you want to split allocating your instance from initializing it). So self
is less likely to collide with something the user might want to define themselves. Also, this is the way it's done in D, which I think is a really nice language (D uses this
instead, but it's the same thing in the end).
How I can recreate this from the outside:
class Foo
a_method()
end
function lexical_function()
end
function self.static_method()
end
function self::static_function()
end
end
class Foo; end
Foo.static_method() end
Foo::static_function() end
-- how we define methods and lexical functions and constructor/self?
class Foo end
function Foo.__members__.a_method() end
getfenv(Foo.__body).lexical_function = function() end
function Foo.static_method() end
function Foo::static_function() end
Note that the getfenv
trick is not guaranteed to work:
class Foo
-- there's no way to get at this from the outside:
local function completely_private()
end
end
$ cat foo.shn
class Foo
a_method()
end
function lexical_function()
end
function self.static_method()
end
function self::static_function()
end
end
$ shinec -o foo.shn
;TvmJIT opcode tree:
(!line "@foo.shn" 1)(!define __magic__ (!index (!call1 require "core") "__magic__"))(!call (!index _G "module") !vararg (!index __magic__ "environ"))
(!line 1) (!assign Foo (!call class "Foo" (!lambda (Foo self super) (!let $#1 __self__)
(!line 2) (!assign (!index (!index self "__members__") "a_method") (!lambda (self) (!if (!not (!call __is__ self $#1)) (!call error (!callmeth "bad argument #1 to '%s' (%s expected got %s)" format (!or (!index (!call1 (!index debug "getinfo") 1 "n") "name") "?") (!call1 tostring $#1) (!call1 typeof self)) 2))))
(!line 4) (!assign lexical_function (!lambda () ))
(!line 6) (!assign (!index self "static_method") (!lambda (self) ))
(!line 8) (!assign (!index self "static_function") (!lambda () )))))
So here the question, other than compatibility with Lua, does shine needs static_methods? Here why, from my ruby OO I'll do:
class HTTPServer; end
-- init method
function HTTPServer(config={})
self.config = config
end
-- instance method
function HTTPServer.start(host, port)
C::signal(C.SIGPIPE, ffi::cast('sig_t', C.SIG_IGN))
server = TCPServer()
server.reuseaddr(true)
server.bind(host, port)
server.listen(128)
header = __make_header()
-- ...
end
-- private
-- or private(HTTPServer.method_name)
function HTTPServer.__make_header() as HTTPHeaders -- don't remember if is allowed this
return {
['Connection'] = 'Keep-Alive'
['Host'] = '127.0.0.1'
['Content-Type'] = 'text/plain'
['Content-Length'] = #mesg
}
end
-- class method
function HTTPServer::start(host, port)
http = self()
http.start(host, port)
return http
end
class HTTPServer
-- init method
function self(config={})
self.config = config
end
-- instance method
function start(host, port)
C::signal(C.SIGPIPE, ffi::cast('sig_t', C.SIG_IGN))
server = TCPServer()
server.reuseaddr(true)
server.bind(host, port)
server.listen(128)
header = __make_header()
-- ...
end
-- private
function __make_header() as HTTPHeaders -- I don't remember if as allowed here
return {
['Connection'] = 'Keep-Alive'
['Host'] = '127.0.0.1'
['Content-Type'] = 'text/plain'
['Content-Length'] = #mesg
}
end
-- class method means HTTPServer::start
function self.start(host, port)
http = self()
http.start(host, port)
return http
end
end
What makes __make_header
private?
Also, Shine doesn't have static methods really. Everything is a table. Including the self
parameter which is in scope in a class body. So saying function self.start() ... end
is identical do doing it from the outside.
What I don't want to lose are lexical functions.
(sorry about the edits, can't type today)
Actually the only declarations which are special in any way are method declarations:
class Foo
instance_method()
end
end
Because I really don't want to be typing function self.__members__.instance_method() ... end
all the time.
mmm, I'm a bit confused. I need to think a bit more about it, from my understand seems Shine can live just with:
class Foo
function a_instance_method() -- equals to: function self.__members__.a_instance_method() ... end
end
-- local will mark lexical functions
function lexical_function() as local -- or local function
end
function self.static_method()
end
function self::static_function()
end
end
Well, yes it could (you'd say local function lexical_function() ... end
- as
is just an alias for setmetatable
). However, as I said, you're overloading function
then. It looks like any other function declaration, but it's not. For example, if I did it your way, then this will surprise you:
class Foo
function some_method()
end
some_method() -- Error: WTF?! I've just defined it above?
end
Because what's really happening is function self.__members__.some_method() end
. That's why I don't want function
to have different meanings in different contexts.
And don't forget the get
and set
declarations which you'd then need to define as:
class Foo
function get some_property()
end
function set some_property()
end
end
Just to close this thread, everything you're suggesting, I've already tried. Here are all my attempts at creating a language on the Lua VM:
So I really have put some thought into it and tried to incorporate all the things I've learned. It's not perfect, no language is, but it's the best set of compromises I can find while still keeping Lua's semantics and performance.
Oki, I see. Thanks a lot for discussing it. Appreciated!
Always happy to discuss. Thanks to you for challenging me!
Actually, you've got me thinking now :)
If I can solve this ambiguity then I'll consider it:
class Foo
local one
function one()
end
function two()
end
one() -- OK: same as local function one() ... end
two() -- Error: I could look it up in the env, but it would have a bad self
end
Hmm... the only way I can see for that to be sane is to either go the Ruby way where everything is an object (so def
always creates a method, even at the top level scope), or not allow arbitrary statements in class bodies (i.e. no lexical class body).
I'm I think I have no know-how here to help you more but I'm not for a Ruby way which I think could add complexity.
I hope is the last, maybe we should setup a chan on freenode.
My questions:
self
and notinit
,initialize
orconstructor
? I'm aware that is not a real constructor butself
is a bit confusing when then you call "methods"self.x
...Shine
context free? I mean, there are different ways to create functions but do you think is possible to have just one?We have:
So we have 3/4 forms to create functions, what I love of langs like
go
is that they haveone way
to do things, and the community love (even too much :D) this.Thanks!