Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Language questions #54

Open
DAddYE opened this issue Apr 8, 2014 · 20 comments
Open

Language questions #54

DAddYE opened this issue Apr 8, 2014 · 20 comments

Comments

@DAddYE
Copy link

DAddYE commented Apr 8, 2014

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
Copy link
Author

DAddYE commented Apr 8, 2014

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
Copy link
Owner

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
Copy link
Owner

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
Copy link
Owner

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
Copy link
Author

DAddYE commented Apr 8, 2014

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
Copy link
Owner

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
Copy link
Owner

$ 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
Copy link
Author

DAddYE commented Apr 8, 2014

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
Copy link
Owner

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
Copy link
Owner

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
Copy link
Author

DAddYE commented Apr 8, 2014

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
Copy link
Owner

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
Copy link
Owner

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
Copy link
Owner

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
Copy link
Author

DAddYE commented Apr 8, 2014

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

@richardhundt
Copy link
Owner

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

@richardhundt richardhundt reopened this Apr 8, 2014
@richardhundt
Copy link
Owner

Actually, you've got me thinking now :)

@richardhundt
Copy link
Owner

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
Copy link
Owner

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
Copy link
Author

DAddYE commented Apr 8, 2014

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants