Calling functions passed as arguments in Lua - functional-programming

I have this code
Option = { }
function Option.nothing( )
local self = { isNone = true, isSome = false }
function self:orElse( alt )
return alt
end
function self:map( f )
return Option.nothing( )
end
function self:exec( f )
end
function self:maybe( alt, f )
return alt
end
return self
end
function Option.just( val )
local self = { isNone = false, isSome = true }
local value = val
function self:orElse( alt )
return value
end
function self:map( f )
return Option.just( f(value) )
end
function self:exec( f )
f( value )
end
function self:maybe( alt, f )
return f(value)
end
return self
end
function printOpt( opt )
local str = opt.maybe( "Nothing", function(s) return "Just " .. s end )
print( str )
end
x = Option.nothing( )
y = Option.just( 4 )
printOpt(x)
printOpt(y)
But I keep getting 'attempt to call local 'f' (a nil value)' here:
function self:maybe( alt, f )
return f(value)
end
It seems I'm having trouble calling a function passed as a argument.

You declared the function as self:maybe(), but you're calling it as opt.maybe(). You should call it as opt:maybe().
Declaring it as self:maybe(alt, f) is equivalent to declaring it as self.maybe(self, alt, f). So if you call it with a . you need 3 args. You're passing 2, so self ends up as "Nothing", and alt ends up as the function object.
However, by calling it as opt:maybe("Nothing", f) this is equivalent to saying opt.maybe(opt, "Nothing", f) which provides the required 3 args.

Related

How to get the tasklist children widgets in button function?

How can I get all children of the tasklist.
I have a tasklist in a function and a tasklist_buttons:
local tasklist_buttons =
gears.table.join(
awful.button(
{},
1,
function(c)
-- HOW TO GET THIS ↓↓↓
-- tasklistChildren = TaskList.children
end
)
)
local TaskList = function(s)
return awful.widget.tasklist(
s,
awful.widget.tasklist.filter.currenttags,
tasklist_buttons,
{},
list_update,
wibox.layout.fixed.horizontal()
)
end
Can't think of a good approach.

Is there a way to use, inside a function, "isdefined(:x)", where x is defined in the same function?

I have a function that looks for a file named "global" in parent directories of the working dir. This is how I imagined it:
function readglobal()
if isfile("./global")
text = readdlm("./global",String,comment_char='/')
else
for i = 1:8
if isfile("../"^i * "global")
text = readdlm("../"^i * "global",String,comment_char='/')
break
end
end
end
isdefined(:text) || error("Could not find global file")
dict = Dict{String,String}()
for i in 1:size(text)[1]
dict[text[i,1]] = text[i,2]
end
return dict
end
This doesn't work because isdefined looks for global variables in the current_module(), where I call the function from.
Is there a way to make it work as intended? To evalute isdefined(:text) inside the function?
I worked around this with the following code, but I think the code above is cleaner.
function readglobal()
foundit = isfile("./global")
if foundit
text = readdlm("./global",String,comment_char='/')
foundit=true
else
for i = 1:8
foundit = isfile("../"^i * "global")
if foundit
text = readdlm("../"^i * "global",String,comment_char='/')
break
end
end
end
foundit || error("Could not find global file")
dict = Dict{String,String}()
for i in 1:size(text)[1]
dict[text[i,1]] = text[i,2]
end
return dict
end
Edit: Somehow I missed that you found the workaround, as you call it, which is pretty much the same as I suggested. I disagree that the first code is cleaner, using isdefined seems hacky to me. A foundit flag is the right way, IMHO.
Original answer:
Don't use isdefined to check whether the file has been found. Instead set a flag, e.g. filefound. Something along these lines (warning, untested):
function readglobal()
filefound = false
filepath = "global"
for i in 0:8
filefound = isfile(filepath)
if filefound
break
end
filepath = joinpath("..", filepath)
end
filefound || error("Could not find file ")
text = readdlm(filepath, String, comment_char='/')
dict = Dict{String,String}()
for i in 1:size(text, 1)
dict[text[i,1]] = text[i,2]
end
return dict
end
Edit 2: Here's a variant:
function readglobal(filename, maxtries=8)
tries = 0
while !isfile(filename) && (tries+=1) <= maxtries
filename = joinpath("..", filename)
end
tries > maxtries || error("Could not find file ")
text = readdlm(filename, ...
...
end
The following is a 1.5-line version of this function:
readglobal() = Dict(mapslices(x->=>(x...),readdlm(first(filter(isfile,
"./$("../"^i)global" for i=0:8)),String;comment_char='/'),2))
It even returns an error if the file is missing ;)
Not an exact answer, but I would use a separate function:
function get_global_content()
if isfile("./global")
return readdlm("./global",String,comment_char='/')
else
for i = 1:8
if isfile("../"^i * "global")
return readdlm("../"^i * "global",String,comment_char='/')
end
end
end
error("Could not find global file")
end
function readglobal()
text = get_global_content()
dict = Dict{String,String}()
for i in 1:size(text)[1]
dict[text[i,1]] = text[i,2]
end
return dict
end
Alternatively, have a look at Nullable, e.g.,
function readglobalnull()
text = Nullable()
if isfile("./global")
text = Nullable(readdlm("./global",String,comment_char='/'))
else
for i = 1:8
if isfile("../"^i * "global")
text = Nullable(readdlm("../"^i * "global",String,comment_char='/'))
break
end
end
end
isnull(text) && error("Could not find global file")
text = get(text)
dict = Dict{String,String}()
for i in 1:size(text)[1]
dict[text[i,1]] = text[i,2]
end
return dict
end
Version 0.7 of Julia has a #isdefined variable_name macro that is able to do precisely what I asked for in this question. It should work for any local variable.

Serialization fails with custom Torch class

Serialization can fails with a class object created containing __pairs:
test = torch.class('test')
function test:__init()
self.data = {}
end
function test:__pairs(...)
return pairs(self.data, ...)
end
function test:get_data()
print(self.data)
end
a = test.new()
a.data = {"asdasd"}
b = torch.serialize(a)
c = torch.deserialize(b)
print(torch.typename(c))
print(c:get_data())
The following returns:
test
nil
The engine behind the torch.serialization is located in the File-class. The File:writeObject us the key function. For the above example the action for a Torch class starts at line 201 with the:
elseif typeidx == TYPE_TORCH then
The type is identified in the File:isWritableObject.
One could probably implement the metatable function write but in the above example the problem was non-torch metatable function __pairs that should be __pairs__ (see torch.getmetatable):
test = torch.class('test')
function test:__init()
self.data = {}
end
function test:__pairs__(...)
return pairs(self.data, ...)
end
function test:get_data()
print(self.data)
end
a = test.new()
a.data = {"asdasd"}
b = torch.serialize(a)
c = torch.deserialize(b)
print(torch.typename(c))
print(c:get_data())
This gives the expected:
test
{
1 : "asdasd"
}

How to call standardGeneric with a variable function name

The following code throws a warning:
test_slot = "testslot"
setGeneric(name = test_slot,
def = function(object){
standardGeneric(test_slot)
},
where = globalenv()
)
[1] "testslot"
Warning message:
In .recursiveCallTest(body, fname) :
the body of the generic function for ‘testslot’ calls 'standardGeneric' to dispatch on a different name ("test_slot")!
I would have expected that it would have been equivalent to:
setGeneric(name = "testslot",
def = function(object){
standardGeneric("testslot")
},
where = globalenv()
)
What happened?
This does not answer why the original block of code failed, but a workaround would be to do:
test_slot = "testslot"
setGeneric(name = test_slot,
def = eval(parse(text = paste0("function(object){",
"standardGeneric('", test_slot, "')",
"}")))
},
where = globalenv()
)
that is, construct the entire def argument to be an evaluated function.

why does the Director class make my global variables not visible to external modules in corona?

I'm doing basic stuff to understand using external classes/modules with global variables, and now I'm trying to add director. But when I added the director class, my global variables such as applyThrust then became not visible in the external class.
It worked fine before refactoring for Director. Before using director, I only had 2 files, rocket.lua and the mainScreen.lua file below was simply main.lua. I used the same globals, applyThrust and thrustForceY, and it worked.
By the way, the director functionality of switching screens appears to work fine, other than this odd side effect of making the global variables not visible.
I asked this on the corona director forums and got no response so I thought I'd try reliable STO.
Here's the files I'm using with the director stuff :
main.lua
display.setStatusBar (display.HiddenStatusBar)
local director = require ("director")
local mainGroup = display.newGroup()
local function main()
mainGroup:insert(director.directorView)
director:changeScene("menu")
return true
end
main()
menu.lua
module(..., package.seeall)
function new()
local localGroup = display.newGroup()
------------------------------------------------------------------------------
local playText = display.newText("Play",160, 100, "Arial", 32)
playText:setTextColor(0, 0, 255)
localGroup:insert(playText)
local function pressPlay (event)
if event.phase == "ended" then
director:changeScene ("mainScreen")
end
end
playText:addEventListener ("touch", pressPlay)
------------------------------------------------------------------------------
return localGroup
end
mainScreen.lua
local Rocket = require( "rocket" )
local physics = require( "physics" )
module(..., package.seeall)
function new()
local localGroup = display.newGroup()
------------------------------------------------------------------
score = 100
physics.start()
local ground = display.newRect( 60, 170, 60, 60 )
physics.addBody( ground, "static", { density=3.0, friction=0.5, bounce=0.2 } )
rocket = Rocket.new(80, 110)
local upText = display.newText("Up",160, 300, "Arial", 32)
upText:setTextColor(0, 0, 255)
------------- my global variables -----------
thrustForceY = -100
applyThrust = false
local function pressUp (event)
local t = event.target
local phase = event.phase
if( "began" == phase ) then
display.getCurrentStage():setFocus( t )
t.isFocus = true
if( not applyThrust ) then
rocket:addThrust()
end
rocket:applyForce(0, thrustForceY, rocket.x, rocket.y)
applyThrust = true
elseif "ended" == phase or "cancelled" == phase then
rocket:removeThrust()
display.getCurrentStage():setFocus( nil )
t.isFocus = false
applyThrust = false
end
return true
end
upText:addEventListener ("touch", pressUp)
----------------------------------------------
return localGroup
end
rocket.lua
module(..., package.seeall)
local physics = require( "physics" )
--constructor--------------------
function new(x, y)
rocket = display.newGroup()
local body = display.newRect( x, y, 25, 60 )
physics.addBody( body, { density=1.5, friction=0.5, bounce=0.2 } )
body.isFixedRotation = true
rocket:insert(body)
local thrust = {}
function rocket:addThrust()
thrust = display.newRect( body.x, body.y + (body.height / 2) , 10, 10 )
thrust.y = body.y + (body.height / 2) + (thrust.height / 2)
physics.addBody( thrust, { density=1.5, friction=0.5, bounce=0.2 } )
rocket:insert(thrust)
end
function rocket:removeThrust()
rocket:remove(thrust)
end
function rocket:applyForce( xForce, yForce, atPointX, atPointY )
body:applyForce( xForce, yForce, atPointX, atPointY )
end
----------------------------------------------------------------------
-- enterFrame listener for rocket
----------------------------------------------------------------------
function rocket:enterFrame (event)
if( applyThrust ) then
body:applyForce(0, thrustForceY, body.x, body.y)
thrust.y = body.y + (body.height / 2) + (thrust.height / 2)
thrust.x = body.x
else
print("applyThrust is nil")
end
end
Runtime:addEventListener("enterFrame", rocket)
return rocket
end
A global variable in Corona is as simple as using "_G." before your variable.
For example:
lives = 3
Would become:
_G.lives = 3
But Director already has functionality to pass parameters to another scene.... there is no need to use globals.
scene1.lua
local params = {
param1= "http://github.com/",
param2 = "bla-bla"
}
director:changeScene( params, "screen2", "moveFromRight" )
screen2.lua
new = function ( params )
local url = params.param1
local bla = params.param2
...
end
Good point.
Also, you can put all your variables in a file you created and access it from any module.

Resources