Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Generics and lambdas

A procedure pointer has one fixed type, and a lambda that captures variables cannot become one. A type parameter has no such limit: a generic with a parameter ( of F ) accepts any value it can call, whether a procedure, a lambda or a closure.

'' F can be any callable: a procedure, a lambda, or a closure.
function CountIf( of T, F )( items() as T, byref test as F ) as int32
    dim as int32 n = 0
    for each x in items
        if test( x ) then n += 1
    next
    return n
end function

sub Transform( of T, F )( items() as T, byref change as F )
    for each byref x in items
        x = change( x )
    next
end sub

dim nums( 0 to 5 ) as int32 = { 3, 8, 1, 9, 4, 7 }

'' A lambda that captures nothing, written at the call.
print "over 5:"; CountIf( nums(), function( byval n as int32 ) as boolean
    return n > 5
end function )

'' A closure: it carries its own copy of limit.
dim as int32 limit = 3
var overLimit = function[ byval limit ]( byval n as int32 ) as boolean
    return n > limit
end function
print "over"; limit; ":"; CountIf( nums(), overLimit )

'' The same generic, with a lambda that returns a new value.
var tenfold = function( byval n as int32 ) as int32
    return n * 10
end function
Transform( nums(), tenfold )
for each n in nums
    print n;
next
print

'' T is now ustring; the lambda's parameter matches it.
dim words( 0 to 2 ) as ustring = { "fig", "banana", "kiwi" }
var fourLetters = function( byref w as ustring ) as boolean
    return len( w ) = 4
end function
print "four letters:"; CountIf( words(), fourLetters )

Output

over 5: 3
over 3: 4
 30 80 10 90 40 70
four letters: 1

CountIf has two type parameters. T is inferred from the array, and F from whatever is passed as test. The only demand the body makes of F is that test( x ) compiles and gives something if can test.

  • The first call passes a lambda written in place. It captures nothing.
  • overLimit captures limit with [ byval limit ], so it is a closure: a small object holding its own copy of limit. It can still be passed, because F becomes the closure's type.
  • Transform takes a lambda that returns a new value, and for each byref writes it back into the array.
  • With words, T is ustring, and the lambda's parameter is a ustring to match.

F is passed byref, so a closure is not copied on the way in. The standard library's containers.ForEach is declared the same way. Lambdas themselves are covered in Procedures.

Activities

  1. Write Sum( of T, F ) that adds up f( x ) for every element, and use it to total the squares of nums.
  2. Capture limit with byref instead, change limit after creating overLimit, and compare the counts.