lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


It was thus said that the Great Petri Häkkinen once stated:
> On Mar 4, 2017, at 8:48 AM, Dirk Laurie <dirk.laurie@gmail.com> wrote:
> 
> >> Why all the trouble? I mean, you could just use free functions:
> >> object_system_func(a, b, c)
> >> 
> >> This is faster (no need for nested dynamic table look ups) and there is no need for "self" parameter that is magically more important than the others. Also you don't need to artificially invent new classes just because you want to add a function that does not naturally fit into your existing class hierarchy.
> > 
> > I agree with all you say — but since 5.2 Lua actually has a VM instruction
> > for object-oriented access, and it does make some kinds of code look
> > cleaner, especially when the object is a userdata but all the other
> > parameters are simple scalars.
> > 
> 
> I'm trying to come up with an example where the OOP style is more cleaner
> (could you give an example?).. e.g. in my mind the syntactic difference
> between the two styles in the following case is irrelevant in practice:
> 
> obj:draw_rectangle(x, y, w, h)
> vs. 
> canvas_draw_rectangle(obj, x, y, w, h)

  I use the OOP syntactic surgar for my userdata to isolate the functions
that work on a particular type to just that type.  For example, my socket
class [1], I have functions that create sockets [2]:

	static const luaL_Reg m_net_reg[] =
	{
	#ifdef __linux__
	  { "interfaces"        , netlua_interfaces     } ,
	#endif
	  { "socket"            , netlua_socket         } ,
	  { "socketfile"        , netlua_socketfile     } ,
	  { "socketfd"          , netlua_socketfd       } ,
	  { "socketpair"        , netlua_socketpair     } ,
	  { "address2"          , netlua_address2       } ,
	  { "address"           , netlua_address        } ,
	  { "addressraw"        , netlua_addressraw     } ,
	  { "htons"             , netlua_htons          } ,
	  { "htonl"             , netlua_htonl          } ,
	  { "ntohs"             , netlua_ntohs          } ,
	  { "ntohl"             , netlua_ntohl          } ,
	  { NULL                , NULL                  }
	};

but I was to isolate the functions that work upon a socket to make it harder
to misuse the API:

	static const luaL_Reg m_sock_meta[] =
	{
	  { "__tostring"        , socklua___tostring    } ,
	  { "__gc"              , socklua_close         } ,
	  { "__index"           , socklua___index       } ,
	  { "__newindex"        , socklua___newindex    } ,
	  { "peer"              , socklua_peer          } ,
	  { "addr"              , socklua_addr          } ,
	  { "bind"              , socklua_bind          } ,
	  { "connect"           , socklua_connect       } ,
	  { "listen"            , socklua_listen        } ,
	  { "accept"            , socklua_accept        } ,
	  { "recv"              , socklua_recv          } ,
	  { "send"              , socklua_send          } ,
	  { "shutdown"          , socklua_shutdown      } ,
	  { "close"             , socklua_close         } ,
	  { "fd"                , socklua_fd            } ,
	  { NULL                , NULL                  }
	};

  To pass in a non-socket item to socklua_accept() you *really need* to go
out of the way to get to the function in Lua.  Also, I'm not polluting the
global namespace, or the socket namespace, with functions that require a
particular userdata to do their job.

> On the other hand when functions take multiple objects as parameters the
> implicit self starts to feel like an oddball. A classical example:
> 
> ray:intersect(box)  -- huh? should the intersect method be in ray or box class? or maybe some sort of intersection_calculator class ;-D
> vs.
> intersect_ray_box(ray, box)

  This point is arguable.  I don't necessarily diagree here.

> Another advantage of the imperative style:
> 
> -- OOP
> 
> for i=1,#objs do
>   objs[i]:do_something()
> end
> 
> function myclass:do_something()
>   -- code here
> end

  Again, if the objs are subtly different there could be different
implementations of do_something().  For instance, the objects could be
sockets, where you have one socket for accepting new connections and several
other sockets that are ongoing connections so the code for do_something()
would be different.

  Encapsulation.

  -spc (Hiding details, stuff like that)

[1]	https://github.com/spc476/lua-conmanorg/blob/master/src/net.c

[2]	https://github.com/spc476/lua-conmanorg/blob/master/src/net.c#L158

[3]	https://github.com/spc476/lua-conmanorg/blob/master/src/net.c#L177