Module:Fun Explained

local p =

local ustring = mw.ustringlocal libraryUtil = require "libraryUtil"local checkType = libraryUtil.checkTypelocal checkTypeMulti = libraryUtil.checkTypeMulti

local iterableTypes =

local _checkCache = local function _check(funcName, expectType) if type(expectType)

"string" then return function(argIndex, arg, nilOk) return checkType(funcName, argIndex, arg, expectType, nilOk) end else -- Lua 5.1 doesn't cache functions as Lua 5.3 does. local checkFunc = _checkCache[funcName] or function(argIndex, arg, expectType, nilOk) if type(expectType)

"table" then if not (nilOk and arg

nil) then return checkTypeMulti(funcName, argIndex, arg, expectType) end else return checkType(funcName, argIndex, arg, expectType, nilOk) end end _checkCache[funcName] = checkFunc return checkFunc endend

-- Iterate over UTF-8-encoded codepoints in string.local function iterString(str) local iter = string.gmatch(str, "[%z\1-\127\194-\244][\128-\191]*") local i = 0 local function iterator i = i + 1 local char = iter if char then return i, char end end return iteratorend

-- funcName and startArg are for argument type-checking.-- The varargs (...) can be either an iterator and its optional state and start-- value, or an iterable type, in which case the function calls the appropriate-- iterator generator function.local function getIteratorTriplet(funcName, startArg, ...) local t = type(...) if t

"function" then return ... end local first = ... checkTypeMulti(funcName, startArg, first, iterableTypes) if t

"string" then return iterString(first) elseif first[1] ~= nil then return ipairs(first) else return pairs(first) endend

function p.chain(func1, func2, ...) return func1(func2(...))end

-- map(function(number) return number ^ 2 end,-- ) --> -- map(function (char) return string.char(string.byte(char) - 0x20) end,-- "abc") --> -- Two argument formats:-- map(func, iterable)-- map(func, iterator[, state[, start_value]])-- func is a function that takes a maximum of two return values of the iterator-- in reverse order. They are supplied in reverse order because the ipairs-- iterator returns the index before the value, but the value is most often more-- important than the index.

-- Any need for map that retains original keys, rather than creating an array?function p.map(func, keepOriginalKeys, ...) checkType("map", 1, func, "function") local iter, state, start_value if type(keepOriginalKeys)

"boolean" then iter, state, start_value = getIteratorTriplet("map", 3, ...) else -- keepOriginalKeys is actually iterator or iterable. iter, state, start_value = getIteratorTriplet("map", 2, keepOriginalKeys, ...) keepOriginalKeys = false end local result = if keepOriginalKeys then for val1, val2 in iter, state, start_value do result[val1] = func(val2, val1, state) end else local i = 0 for val1, val2 in iter, state, start_value do i = i + 1 result[i] = func(val2, val1, state) end end return resultend

p.mapIter = p.map

local function fold(func, result, ...) checkType("fold", 1, func, "function") local iter, state, start_value = getIteratorTriplet("fold", 3, ...) for val1, val2 in iter, state, start_value do result = func(result, val2, val1, state) end return resultendp.fold = fold

function p.count(func, ...) checkType("count", 1, func, "function") return fold(function (count, val) if func(val) then return count + 1 end return count end, 0, ...)end

function p.forEach(func, ...) checkType("forEach", 1, func, "function") local iter, state, start_value = getIteratorTriplet("forEach", 2, ...) for val1, val2 in iter, state, start_value do func(val2, val1, state) end return nilend

--------------------------------------------------- From http://lua-users.org/wiki/CurriedLua.-- reverse(...) : take some tuple and return a tuple of elements in reverse order---- e.g. "reverse(1,2,3)" returns 3,2,1local function reverse(...) -- reverse args by building a function to do it, similar to the unpack example local function reverseHelper(acc, v, ...) if select("#", ...)

0 then return v, acc else return reverseHelper(function return v, acc end, ...) end end -- initial acc is the end of the list return reverseHelper(function return end, ...)end

function p.curry(func, numArgs) -- currying 2-argument functions seems to be the most popular application numArgs = numArgs or 2 -- no sense currying for 1 arg or less if numArgs <= 1 then return func end -- helper takes an argTrace function, and number of arguments remaining to be applied local function curryHelper(argTrace, n) if n

0 then -- kick off argTrace, reverse argument list, and call the original function return func(reverse(argTrace)) else -- "push" argument (by building a wrapper function) and decrement n return function(onearg) return curryHelper(function return onearg, argTrace end, n - 1) end end end -- push the terminal case of argTrace into the function first return curryHelper(function return end, numArgs)end

-------------------------------------------------

-- some(function(val) return val % 2

0 end,-- ) --> truefunction p.some(func, ...) checkType("some", 1, func, "function") local iter, state, start_value = getIteratorTriplet("some", 2, ...) for val1, val2 in iter, state, start_value do if func(val2, val1, state) then return true end end return falseend

-- all(function(val) return val % 2

0 end,-- ) --> truefunction p.all(func, ...) checkType("some", 1, func, "function") local iter, state, start_value = getIteratorTriplet("all", 2, ...) for val1, val2 in iter, state, start_value do if not func(val2, val1, state) then return false end end return trueend

function p.indexOf(func, ...) local iter, state, start_value = getIteratorTriplet("indexOf", 2, ...) if type(func)

"function" then for val1, val2 in iter, state, start_value do if func(val2, val1, state) then return val1 end end -- func is actually value to search for. -- Not a great idea to combine these two separate functions. elseif func ~= nil then -- check for NaN? for val1, val2 in iter, state, start_value do if func

val2 then return val1 end end else error("value to search for is nil") end return nilend

function p.filter(func, ...) local check = _check checkType("filter", 1, func, "function") local new_t = local new_i = 0 local iter, state, start_value = getIteratorTriplet("filter", 2, ...) for val1, val2 in iter, state, start_value do if func(val2, val1, state) then new_i = new_i + 1 new_t[new_i] = val1 end end return new_tend

function p.range(low, high) low = low - 1 return function if low < high then low = low + 1 return low end endend

--------------------------------- Fancy stufflocal function capture(...) local vals = return function return unpack(vals) endend

-- Log input and output of function.-- Receives a function and returns a modified form of that function.function p.logReturnValues(func, prefix) return function(...) local inputValues = capture(...) local returnValues = capture(func(...)) if prefix then mw.log(prefix, inputValues) mw.log(returnValues) else mw.log(inputValues) mw.log(returnValues) end return returnValues endend

p.log = p.logReturnValues

-- Convenience function to make all functions in a table log their input and output.function p.logAll(t) for k, v in pairs(t) do if type(v)

"function" then t[k] = p.logReturnValues(v, tostring(k)) end end return tend

----- M E M O I Z A T I O N------- metamethod that does the work-- Currently supports one argument and one return value.local func_key = local function callMethod(self, x) local output = self[x] if not output then output = self[func_key](x) self[x] = output end return outputend

-- shared metatablelocal mt =

-- Create callable table.function p.memoize(func) return setmetatable(mt)end

-------------------------------

return p