Up to Main Index Up to Journal for March, 2025 JOURNAL FOR THURSDAY 20TH MARCH, 2025 ______________________________________________________________________________ SUBJECT: Lambda functions and higher order filter, map, reduce DATE: Fri 21 Mar 04:30:34 GMT 2025 > A quick side note: Most of my free time recently has been spent trying to > find a new job. The majority of the feedback has been absent, not even a > rejection. So far, 9 weeks job hunting and not a single interview :( If any > of my readers can help, I’m looking for a 100% remote position as a senior > or lead Go developer. If possible, I’d like to continue my work integrating > and building solutions using LLMs and AI. Day-to-day I use Mote for scripting and quickly whipping up algorithms I want to play with. Development of Mote mostly consists of writing the tests for the new uint and float data types at the moment. While essential work, it’s also monotonous and boring. However, I found myself needing Lambda, or Anonymous, functions. So, for a change of pace I spent an afternoon last weekend adding Lambda functions to Mote :) A quick, simple example: # displays the approximate square root of n[0.0…10.0] using the # iterative Heron's / Babylonian method. for x=0.0; x<10.0; x++ printf "√%g = %2.2f\n" x lambda x -> n a b <- 1.0 n for ; abs(a-b) > 1e-9; a = (a+b) / 2.0 b = n/a next return a end next When run, this produces: √0 = 0.00 √1 = 1.00 √2 = 1.41 √3 = 1.73 √4 = 2.00 √5 = 2.24 √6 = 2.45 √7 = 2.65 √8 = 2.83 √9 = 3.00 The lambda function is invoked directly using “arg… -> param…” to pass arguments to the lambda’s parameters. The ‘->’ operator is analogous to the multiple assignment ‘<-’ operator. Also see the later examples of using filter, map and reduce higher order functions where the lambda is not invoked directly. There are a few caveats with the current lambda implementation. The main one is that a lambda function can only be in-lined if it is the last argument to an operator, built-in or function. Otherwise it has to be assigned to a variable and the variable used. Secondly, lambda do not capture their scope. This means they currently cannot be used to create closures. Having to have lambda as the last argument leads to some awkward code. For example, it would be nice to be able to write: sm = [string]( "add" lambda x y return x+y end, ) Instead we have to write: sm = [string]() sm["add"] = lambda x y return x+y end This is because the body of the lambda is left in-situ and routed around, like a normal function definition. Taking the previous example this is rewritten to the equivalent of: sm = [string]() sm["add"] = Λ0 goto ∙0∙G Λ0: func x y return x + y end ∙0∙G: Here we can see the generated guarding goto, which prevents the function from being executed unless explicitly called. We can also see the internal name generated for the lambda function ‘Λ0’. For this first iteration, lambda functions work for a lot of use cases. The limitations can be worked on and improved at a later date. One good use case is to implement filter, map and reduce as higher order functions. A higher order function[1] is just a function that takes a function as an argument or returns a function. The three functions are described next with example ‘fn’ lambda functions: • filter — applies a function, fn, to each element in an array. Returns an array containing only those elements where fn returns true. The function fn should be defined with a single parameter to receive the current element’s value and return true or false. lambda x return x ~m `g$` end • map — applies a function, fn, to each element in an array. Returns an array where each element is replaced with the result of calling fn. The function fn should be defined with a single parameter to receive the current element’s value and return the new value for the element. lambda x return x ~u `^.` end • reduce — applies a function to each element in an array, except the first element. Returns a single accumulated value. The function fn should be defined with two parameters: the currently accumulated value and the current element’s value. fn should return the new accumulated value. The accumulated value will initially be set to the value of the first element. lambda acc x return acc += " "+x end Here are my implementations of filter, map and reduce: func filter data fn list = dim data[0] 0 range ; v; data is @fn v; list[] = v next return list end func map data fn range x; d; data data[x] = @fn d next return data end func reduce data fn acc = delete data 0 range ; d; data acc = @fn acc d next return acc end Here are some examples of using filter, map and reduce with lambda functions: # String array of test data. sa = []string "ant" "bat" "cat" "dog" "emu" "fly" "gnu" "hog" # Apply filter selecting only values that end with a 'g'. # Displays: ["dog", "hog"] println @filter sa lambda x return x ~m `g$` end # Apply map to uppercase the first letter of each value. # Displays: ["Ant", "Bat", "Cat", "Dog", "Emu", "Fly", "Gnu", "Hog"] println @map sa lambda x return x ~u `^.` end # Apply reduce to data array to return a space separated string of values. # Displays: ant bat cat dog emu fly gnu hog println @reduce sa lambda acc x return acc+" "+x end The filter, map & reduce functions work with regular, boring functions too :) func ccat acc x return acc+x end println @reduce []string("B" "y" "e" "!") ccat When run, this code displays: Bye! -- Diddymus [1] https://en.wikipedia.org/wiki/Higher-order_function Up to Main Index Up to Journal for March, 2025