Syntax sugar for default arguments

classic Classic list List threaded Threaded
23 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Syntax sugar for default arguments

Dirk Laurie-2
    function f(x,y=0)
    function f(x,y or 0)

to mean

    function f(x,y) y=y or 0

Neither would break existing code.

Which of the above should I choose to implement as a patch?

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Mark Gabby

I prefer 2, since it seems closer to the meaning and therefore more intuitive. But does it really need a patch?

Yea, it seems 2 works right now ;)

On Apr 20, 2013 11:41 PM, "Dirk Laurie" <[hidden email]> wrote:
    function f(x,y=0)
    function f(x,y or 0)

to mean

    function f(x,y) y=y or 0

Neither would break existing code.

Which of the above should I choose to implement as a patch?

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Mark Gabby
In reply to this post by Dirk Laurie-2

To whit:

local function f(t, var)
print(var)
end

f(3, x or 2)

Prints 2 in Lua 5.1. Wouldn't have thought of doing that though, thanks for the idea! This is a great idiom.

(Am I missing something? Would your patch catch some case this wouldn't work with?)

On Apr 20, 2013 11:41 PM, "Dirk Laurie" <[hidden email]> wrote:
    function f(x,y=0)
    function f(x,y or 0)

to mean

    function f(x,y) y=y or 0

Neither would break existing code.

Which of the above should I choose to implement as a patch?

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Choonster TheMage
On Sun, Apr 21, 2013 at 4:55 PM, Mark Gabby <[hidden email]> wrote:

> To whit:
>
> local function f(t, var)
> print(var)
> end
>
> f(3, x or 2)
>
> Prints 2 in Lua 5.1. Wouldn't have thought of doing that though, thanks for
> the idea! This is a great idiom.
>
> (Am I missing something? Would your patch catch some case this wouldn't work
> with?)
>
> On Apr 20, 2013 11:41 PM, "Dirk Laurie" <[hidden email]> wrote:
>>
>>     function f(x,y=0)
>>     function f(x,y or 0)
>>
>> to mean
>>
>>     function f(x,y) y=y or 0
>>
>> Neither would break existing code.
>>
>> Which of the above should I choose to implement as a patch?
>>
>

I think Dirk wants to patch the behavior into the function definition,
not the function call.

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Mark Gabby

Ahh, of course! Silly me. Thanks for the clarification.

On Apr 20, 2013 11:57 PM, "Choonster TheMage" <[hidden email]> wrote:
On Sun, Apr 21, 2013 at 4:55 PM, Mark Gabby <[hidden email]> wrote:
> To whit:
>
> local function f(t, var)
> print(var)
> end
>
> f(3, x or 2)
>
> Prints 2 in Lua 5.1. Wouldn't have thought of doing that though, thanks for
> the idea! This is a great idiom.
>
> (Am I missing something? Would your patch catch some case this wouldn't work
> with?)
>
> On Apr 20, 2013 11:41 PM, "Dirk Laurie" <[hidden email]> wrote:
>>
>>     function f(x,y=0)
>>     function f(x,y or 0)
>>
>> to mean
>>
>>     function f(x,y) y=y or 0
>>
>> Neither would break existing code.
>>
>> Which of the above should I choose to implement as a patch?
>>
>

I think Dirk wants to patch the behavior into the function definition,
not the function call.

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Victor Bombi
In reply to this post by Dirk Laurie-2
I like both.
I made an utility function ("assign") for that and for giving a value to a
variable in a call to function (without leaning on order)
It is not syntactic sugar because the construction is longer but is more
powerful
must be called with an array as argument when you want defaults
Hope anyone likes!!
Best
victor bombi
--------------
--allow defaults as in:
--function fun4d(defval)
-- local an,en,inp = assign({"an","en","inp"},{1,2,3},defval)
-- print("fun4d",an,en,inp)
--end
--fun4d{en=67,5}

local function assign(defs,defv,...)
 local defval
 if select('#', ...)==1 and type(select(1, ...))=="table" and
getmetatable(select(1, ...)) == nil then
  defval = select(1, ...)
 else
  defval = {...}
 end
 local def ={}
 --assign default
 for k,v in ipairs(defs) do
  def[v]=defv[k]
 end
 --assign integer indexed
 for k,v in pairs(defval) do
  --print("def2a ",k,v)
  if type(k)=="number" and math.floor(k)==k then
   if defs[k] then
    def[defs[k]]=v
   else
    error("bad arg index:"..k)
   end
  else
   def[k]=v
  end
 end
 local ret = {}
 for i,v in ipairs(defs) do
  ret[i] = def[defs[i]]
 end
 return unpack(ret)
end
----- Original Message -----
From: "Dirk Laurie" <[hidden email]>
To: "Lua mailing list" <[hidden email]>
Sent: Sunday, April 21, 2013 8:41 AM
Subject: Syntax sugar for default arguments


>    function f(x,y=0)
>    function f(x,y or 0)
>
> to mean
>
>    function f(x,y) y=y or 0
>
> Neither would break existing code.
>
> Which of the above should I choose to implement as a patch?
>


Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Thomas Jericke
In reply to this post by Dirk Laurie-2
Though question,
The 1. version
  function f(x,y=0)
has the advantage that it shows the assignment or initialization of y with 0, but it
does not show, that this only happens if y == nil, the condition part is not shown. It also has the advantage that is close to other languages.

the 2.
function f(x,y or 0)
Shows that there is a condition but not the initialization. It is similar to how Lua handles this in other situations.

Maybe it would be better if there was syntactic sugar for conditional assignments and use this syntax then here as well:

y or = 0
  -> if not y then y = 0 end
t.x or = "hello"
  -> if not t.x then t.x = "hello" end
This is different from y = y or 0 as the assignment  in here always happens, it's just you end up with the same value in y if y ~= nil.
In y or = 0, the assignment is really only done if y is nil.

Now the 2nd step would be to allow this syntax in the function declaration.
function f(x, y or = 0)

Now I don't think "or = " is very beautiful, but I don't have a nicer idea at the moment, and it shows both, the condition and the assignment.

--
Thomas

-----Original Message-----

> From: "Dirk Laurie" <[hidden email]>
> To: "Lua mailing list" <[hidden email]>
> Date: 21-04-2013 08:43
> Subject: Syntax sugar for default arguments
>
> function f(x,y=0)
>     function f(x,y or 0)
>
> to mean
>
>     function f(x,y) y=y or 0
>
> Neither would break existing code.
>
> Which of the above should I choose to implement as a patch?




Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Steve Litt
In reply to this post by Dirk Laurie-2
On Sun, 21 Apr 2013 08:41:31 +0200
Dirk Laurie <[hidden email]> wrote:

>     function f(x,y=0)
>     function f(x,y or 0)
>
> to mean
>
>     function f(x,y) y=y or 0
>
> Neither would break existing code.
>
> Which of the above should I choose to implement as a patch?

I prefer the first one. It's shorter, which is good for keystrokes and
great for line real estate, and it's familiar from other languages.

By the way, are you going to let the other shoe drop and do the same
thing in the subroutine *call*?

function f(x=3, y=4)
   whatever()
end

f(y=7)   -- no need to articulate x

Thanks,

SteveT

Steve Litt                *  http://www.troubleshooters.com/
Troubleshooting Training  *  Human Performance

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Dirk Laurie-2
2013/4/21 Steve Litt <[hidden email]>:

> By the way, are you going to let the other shoe drop and do the same
> thing in the subroutine *call*?
>
> function f(x=3, y=4)
>    whatever()
> end
>
> f(y=7)   -- no need to articulate x

This is immeasurably harder. The other one is mere syntax sugar, a trivial
modification to the source string before passing it along.

This requires code to examine run-time debugging information. At compile
time it is not known which function object is currently associated with the
name f. Therefore it is not known which parameter of f was called y when
the function was defined. One will have to generate code that repeatedly
calls debug.getlocal and works through all the local variables one by one.

All this, for a simple function call.

What will be easier is default values for entries in keyword-style
table arguments:

function f{x=3,y=4}
  whatever()
end

This will translate to the equivalent of:

function f(param)
  for k,v in pairs(default[f]) do f[k]=f[k] or v end
  whatever()
end
default[f]={x=3,y=4}



default_parameters[f]

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Enrico Colombini
By the way... having a default argument for a boolean:

   function Show(show=true)
     -- ...
   end

   Show(nil)   --> Show(true)  -- use default
   Show(true)  --> Show(true)  -- arg not nil, use arg
   Show(false) --> Show(false) -- arg not nil, use arg

should behave differently from simply using 'or' on the argument:

   function Show(show)
     show = show or true -- (not a good idea!)
     -- ...
   end

   Show(nil)   --> Show(true) -- use default
   Show(true)  --> Show(true) -- arg not nil/false, use arg
   Show(false) --> Show(true) -- unintentionally use default (oops)

--
   Enrico

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Steve Litt
In reply to this post by Dirk Laurie-2
On Sun, 21 Apr 2013 19:45:46 +0200
Dirk Laurie <[hidden email]> wrote:

> 2013/4/21 Steve Litt <[hidden email]>:
>
> > By the way, are you going to let the other shoe drop and do the same
> > thing in the subroutine *call*?
> >
> > function f(x=3, y=4)
> >    whatever()
> > end
> >
> > f(y=7)   -- no need to articulate x
>
> This is immeasurably harder. The other one is mere syntax sugar, a
> trivial modification to the source string before passing it along.

Never mind then. Luckily, Lua features tables so:

function whatever(t)
  for k,v in pairs(t) do
    print(k,' : ', v)
  end
end

function f(t)
  t.x=t.x or 3
  t.y=t.y or 4
  whatever(t)
end

local t={}
t.y=7
f(t)


This is how I wrote my infamous "Relevant Lines" continue statement
workaround -- zillions of defaults in a table made it useful under
widely varying circumstances, and also made it spectacular for break
logic:

http://www.troubleshooters.com/codecorn/lua/luaclosures.htm#_A_Practical_Line_Skipper_Iterator

Thanks,

SteveT

Steve Litt                *  http://www.troubleshooters.com/
Troubleshooting Training  *  Human Performance

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Sven Olsen
In reply to this post by Enrico Colombini


  Show(nil)   --> Show(true)  -- use default
  Show(true)  --> Show(true)  -- arg not nil, use arg
  Show(false) --> Show(false) -- arg not nil, use arg

Seconded.  It seems like the right semantic in this case is:

function f(x,y=0) --> function f(x,y)  y= y==nil and 0 or y;

That's still fairly easy to patch, but, has a behavior that's closer to what a C++ programmer might expect.

Are you planning to allow things other than constants to be used as defaults?

If so, watch out for the potential trouble that can happen near self-referential expressions.  

local y={1,2,3}
function f(x=f, y=y[2]) --> naive implementations may segfault here

Peter's old table unpack patch still has a bug in the 'local a in a' case.  Which reminds me, I should really get around to posting a fix for that on the wiki...

-Sven
Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Sven Olsen


function f(x,y=0) --> function f(x,y)  y= y==nil and 0 or y;


Ok, I'm wrong.  There's a bug in my semantic. What you actually want is probably:

function f(x,y=<const expression>) --> if y ~= nil then y= <const expression> end

Piggybacking on OP_OR or OP_AND just leads to all sorts of trouble in the boolean cases.

-Sven
Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Tim Hill
In reply to this post by Dirk Laurie-2
Why can't you just use a wrapper function? Trivial to do, and doesn't need any patching:

function g(x, y)
        f(x, y or 0)
end

And of course, it's easy to wrap transparently:

do
        local fo = f
        function f(x, y)
                fo(x, y or 0)
        end
end

--Tim

On Apr 20, 2013, at 11:41 PM, Dirk Laurie <[hidden email]> wrote:

>    function f(x,y=0)
>    function f(x,y or 0)
>
> to mean
>
>    function f(x,y) y=y or 0
>
> Neither would break existing code.
>
> Which of the above should I choose to implement as a patch?
>


Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Coda Highland
On Sun, Apr 21, 2013 at 6:31 PM, Tim Hill <[hidden email]> wrote:

> Why can't you just use a wrapper function? Trivial to do, and doesn't need any patching:
>
> function g(x, y)
>         f(x, y or 0)
> end
>
> And of course, it's easy to wrap transparently:
>
> do
>         local fo = f
>         function f(x, y)
>                 fo(x, y or 0)
>         end
> end
>
> --Tim
>
> On Apr 20, 2013, at 11:41 PM, Dirk Laurie <[hidden email]> wrote:
>
>>    function f(x,y=0)
>>    function f(x,y or 0)
>>
>> to mean
>>
>>    function f(x,y) y=y or 0
>>
>> Neither would break existing code.
>>
>> Which of the above should I choose to implement as a patch?
>>
>
>

I'd rather do it in the language instead of in script, because you may
still care about the difference between nil and not having passed a
parameter at all.

/s/ Adam

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

steve donovan
On Mon, Apr 22, 2013 at 3:47 AM, Coda Highland <[hidden email]> wrote:
I'd rather do it in the language instead of in script, because you may
still care about the difference between nil and not having passed a
parameter at all.

Well, you can always switch to a different dialect ;)  (I've used the 'puzzlement command' ?que to dump out the actual generated Lua)

$ mooni
MoonScript version 0.2.3
Note: use backslash at line end to start a block
> f = (x,y=2) -> print x,y
> ?que
f = function(x, y)
  if y == nil then
    y = 2
  end
  return print(x, y)
end
> f 10
10      2

Moonscript has become the happy home of many language proposals from lua-l, but this is not a criticism of Lua's minimalism, which generally is a good thing (I only miss short-function forms really)

Although (more seriously) is that important to have this sugar?  Surely it is a documentation issue?  E.g. LDoc supports this style of annotation:

-------
-- A useful function
-- @int x  first!
-- @int [opt=2]  second!
function f (x,y)
   if y == nil then y = 2 end
   print(x,y)
end

steve d.

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Tim Hill
In reply to this post by Coda Highland
Well my personal opinion is it's bad design to do that anyway. I always train people to think of a Lua function as one that has as input an infinite number of arguments, most of which are nil, and returns an infinite number of values, again most of which are nil.

--Tim


On Apr 21, 2013, at 6:47 PM, Coda Highland <[hidden email]> wrote:

> On Sun, Apr 21, 2013 at 6:31 PM, Tim Hill <[hidden email]> wrote:
>> Why can't you just use a wrapper function? Trivial to do, and doesn't need any patching:
>>
>> function g(x, y)
>>        f(x, y or 0)
>> end
>>
>> And of course, it's easy to wrap transparently:
>>
>> do
>>        local fo = f
>>        function f(x, y)
>>                fo(x, y or 0)
>>        end
>> end
>>
>> --Tim
>>
>> On Apr 20, 2013, at 11:41 PM, Dirk Laurie <[hidden email]> wrote:
>>
>>>   function f(x,y=0)
>>>   function f(x,y or 0)
>>>
>>> to mean
>>>
>>>   function f(x,y) y=y or 0
>>>
>>> Neither would break existing code.
>>>
>>> Which of the above should I choose to implement as a patch?
>>>
>>
>>
>
> I'd rather do it in the language instead of in script, because you may
> still care about the difference between nil and not having passed a
> parameter at all.
>
> /s/ Adam
>


Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Andrew Starks
Where all of this goes wrong is when I think of tables and up values. If you said that default arguments must always be literals, that *might* solve something. it gets hairy when you contemplate:

a = function(arg1 = 3, arg2 = {"first value", x = d, } or old_value or error("arg2 is required"))

What happens if arg2 is set, but the first value and/or x is not set? Are we saying, if no table, put in these defaults and if there is a table, don't put any defaults in, or fill in the missing defaults? Where is the boundary of defaults? Is it enough to just say that defaults are not recursive and that if anything is provided, than the entire default argument is discarded?

There are two topics that keep getting brought up and I think that it's repeated because there is a genuine need to clean up some common and tedious idioms. First is the frequent:

local table, string, etc = table, string, etc

second this one, which is to eliminate:

a = function(arg1, arg2)
   arg1= arg1 or 3,
  arg2 = arg2 or {"first value", x=d} or old_value or error("arg2 is required")

I think that the most compelling case is that there is no equivalent assignment syntax to the "lone table argument" calling syntax. If default arguments were allowed only for the "lone table argument" variety, this would be very welcomed and to my thinking, would be a "Lua amount of sugar." Something like:

a=function{arg1 = arg 2 = {(as before)} or (as before)}
  assert(type(arg) == "table", "The only way to see this is if the user passes in a non-table as the first argument")

  --or alternately

assert(type(({...})[1] == "table", (as before))

The only reason that I think the former might be better is because it distinguishes between the normal meaning of "..." (unknown number and type of arguments) and the fact that we really do expect a table as the first argument, in this case.

Okay, I know that others are tired of this topic. I'd only remind the annoyed that perhaps it is a sign that there is an improvement in simplicity to be found somewhere in these areas of interest.

-Andrew


On Thu, Apr 25, 2013 at 12:48 AM, Tim Hill <[hidden email]> wrote:
Well my personal opinion is it's bad design to do that anyway. I always train people to think of a Lua function as one that has as input an infinite number of arguments, most of which are nil, and returns an infinite number of values, again most of which are nil.

--Tim


On Apr 21, 2013, at 6:47 PM, Coda Highland <[hidden email]> wrote:

> On Sun, Apr 21, 2013 at 6:31 PM, Tim Hill <[hidden email]> wrote:
>> Why can't you just use a wrapper function? Trivial to do, and doesn't need any patching:
>>
>> function g(x, y)
>>        f(x, y or 0)
>> end
>>
>> And of course, it's easy to wrap transparently:
>>
>> do
>>        local fo = f
>>        function f(x, y)
>>                fo(x, y or 0)
>>        end
>> end
>>
>> --Tim
>>
>> On Apr 20, 2013, at 11:41 PM, Dirk Laurie <[hidden email]> wrote:
>>
>>>   function f(x,y=0)
>>>   function f(x,y or 0)
>>>
>>> to mean
>>>
>>>   function f(x,y) y=y or 0
>>>
>>> Neither would break existing code.
>>>
>>> Which of the above should I choose to implement as a patch?
>>>
>>
>>
>
> I'd rather do it in the language instead of in script, because you may
> still care about the difference between nil and not having passed a
> parameter at all.
>
> /s/ Adam
>



Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Andrew Starks
In reply to this post by Enrico Colombini

On Sun, Apr 21, 2013 at 3:21 PM, Enrico Colombini <[hidden email]> wrote:
By the way... having a default argument for a boolean:

<snip/> 
  Show(nil)   --> Show(true) -- use default
  Show(true)  --> Show(true) -- arg not nil/false, use arg
  Show(false) --> Show(true) -- unintentionally use default (oops)

--
  Enrico


This is my most hated bug. I feel like I can probably find this bug in just about every piece of lua code, even though finding it doesn't mean that the expected arguments wouldn't be processed correctly. That is, usually the caller is expected to pass a non-bool or nothing, and false would act as a lot like it's supposed to, but not always.

It always takes me too long to figure out the correct way around it, when I may want to accept many things, including a boolean, especially when I want to default to "false".

show = show ~= nil and show or false

That's fine enough, but not especially clear and requires a bit too much thinking.

-Andrew 

Reply | Threaded
Open this post in threaded view
|

Re: Syntax sugar for default arguments

Dirk Laurie-2
In reply to this post by Andrew Starks
2013/4/25 Andrew Starks <[hidden email]>:

> a = function(arg1 = 3, arg2 = {"first value", x = d, } or old_value or
> error("arg2 is required"))

Just to clarify: my original intention was not to provide keyword arguments,
but to provide compile-time default values. For example:

function claim(condition,message="Warning",log=io.stderr)
   if not condition then log:write(where(2),message,'\n') end
end

which might be called as

claim(x>0)
claim(x>0,"This will only work if x>0")
claim(x>0,nil,mylogfile)
claim(nil,"Header for default error log")

And I have by now got an answer: people prefer `=` over `or`.

12