richardhundt / shine

A Shiny Lua Dialect
Other
231 stars 18 forks source link

Language questions #54

Open DAddYE opened 10 years ago

DAddYE commented 10 years ago

I hope is the last, maybe we should setup a chan on freenode.

My questions:

  1. Why the "constructor" is self and not init, initialize or constructor? I'm aware that is not a real constructor but self is a bit confusing when then you call "methods" self.x ...
  2. What will you do to make Shine context free? I mean, there are different ways to create functions but do you think is possible to have just one?

We have:

class Fiber
   self::idle = function() end
   function self.nready()
      return #ready
   end

   ready()
      if not self.queued then
         ready[#ready + 1] = self
         self.queued = true
      end
      self.active = true
   end

   foo()
      return async =>
         print "server listening on http://%{host}:%{port}/"
         while true do
            client = server.accept()
            async =>
               filter = HTTPFilter(StreamReader(client, nil, 1024))
            end
         end
       end
    end
    ...

So we have 3/4 forms to create functions, what I love of langs like go is that they have one way to do things, and the community love (even too much :D) this.

Thanks!

DAddYE commented 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.

richardhundt commented 10 years ago

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.

richardhundt commented 10 years ago

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
richardhundt commented 10 years ago

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).

DAddYE commented 10 years ago

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?
richardhundt commented 10 years ago
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
richardhundt commented 10 years ago
$ 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 () )))))
DAddYE commented 10 years ago

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
richardhundt commented 10 years ago

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)

richardhundt commented 10 years ago

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.

DAddYE commented 10 years ago

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
richardhundt commented 10 years ago

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.

richardhundt commented 10 years ago

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
richardhundt commented 10 years ago

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.

DAddYE commented 10 years ago

Oki, I see. Thanks a lot for discussing it. Appreciated!

richardhundt commented 10 years ago

Always happy to discuss. Thanks to you for challenging me!

richardhundt commented 10 years ago

Actually, you've got me thinking now :)

richardhundt commented 10 years ago

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
richardhundt commented 10 years ago

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).

DAddYE commented 10 years ago

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.