11
\$\begingroup\$

I have been building a small library of VBA functions. I have a small section with higher order functions that I'd like to share. It uses strings as delegates/function pointers and Application.Run to actually call the functions.

This isn't by any means a true implementation; just a quick and dirty implementation without connecting external libraries or creating objects for every function. I haven't found real drawbacks yet. Application.Run always passes arguments by value, which would be a draw back, but is desirable in all of these implementations. Also not being able to declare lambdas is a buzz-kill.

Public Sub Map(ByVal delegate As String, ByRef sequence As Variant)
 Dim i As Long
 For i = LBound(sequence) To UBound(sequence)
 sequence(i) = Application.Run(delegate, sequence(i))
 Next i
End Sub
Public Function Mapped(ByVal delegate As String, ByVal sequence As Variant) As Variant
 Mapped = sequence
 Map delegate, Mapped
End Function
Public Function Fold(ByVal delegate As String, ByVal sequence As Variant, _
 ByVal initial_value As Variant) As Variant
 Fold = initial_value
 Dim el As Variant
 For Each el In sequence
 Fold = Application.Run(delegate, Fold, el)
 Next el
End Function
Public Function Reduce(ByVal delegate As String, ByVal sequence As Variant) As Variant
 ' Head and Tail return the first element and the rest, respectively.
 Reduce = Fold(delegate, Tail(sequence), Head(sequence))
End Function
Public Function Compose(ByVal delegates As Variant, ByVal initial_value As Variant) As Variant
 Compose = initial_value
 Dim delegate As Variant
 For Each delegate In delegates
 Compose = Application.Run(delegate, Compose)
 Next delegate
End Function
asked Aug 14, 2014 at 21:31
\$\endgroup\$
2
  • 2
    \$\begingroup\$ When you say "without connecting external libraries" did you mean something like codeproject.com/Articles/204509/Functors-in-VBA \$\endgroup\$ Commented Aug 16, 2014 at 6:38
  • \$\begingroup\$ Yes. However that is a far better library than the one's I had had heard about \$\endgroup\$ Commented Aug 16, 2014 at 15:11

2 Answers 2

12
\$\begingroup\$

Things I like

  • This idea.
  • You've explicitly declared ByVal and ByRef.
  • All methods are short, clear, and concise.

Things to watch out for

  • All of these functions take variants in as parameters, by necessity. Client code runs the risk of passing in a sequence that isn't enumerable. These really need a comment and maybe a VB_Description attribute describing what kind of arguments the functions expect.
  • You also may want to consider handling runtime error 438 "Object doesn't support Method" for the functions that use For Each loops. There's not much you can do other than return a more meaningful error message though.
  • If you change the loop in Map to a For Each and manually increment i, all of the functions will be compatible with collections as well as arrays.

Nitpicks

  • Underscores have a special meaning. They indicate event procedures. So I would change initial_value to initialValue.
  • While I quickly understood that el stood for element, there's no reason not to just spell it out.
answered Aug 15, 2014 at 1:03
\$\endgroup\$
1
  • \$\begingroup\$ If the loop in Map is changed to a For Each and i is manually incremented, then one should declare Option Base 1 since Arrays are zero-indexed by default, while Collections are one-indexed. \$\endgroup\$ Commented Feb 19, 2024 at 1:31
3
\$\begingroup\$

you have extra newlines in your for loops, and other blocks of code, like this

For Each el In sequence
 Fold = Application.Run(delegate, Fold, el)
Next el

Remove them

For Each el In sequence
 Fold = Application.Run(delegate, Fold, el)
Next el

There isn't the need for them.

your functions and subs also look like this,

Public Function Mapped(ByVal delegate As String, ByVal sequence As Variant) As Variant
 Mapped = sequence
 Map delegate, Mapped
End Function

it looks like you think something needs to be added but you didn't put in any comments to say what you are thinking about...

Public Function Mapped(ByVal delegate As String, ByVal sequence As Variant) As Variant
 Mapped = sequence
 Map delegate, Mapped
End Function

is what this should look like.

answered Aug 15, 2014 at 15:10
\$\endgroup\$
3
  • 5
    \$\begingroup\$ Am I correct in assuming this is just a personal preference? I did remove blank lines until I was instructed to do differently. To be honest, I rather like using new lines to delineate different logical steps. \$\endgroup\$ Commented Aug 15, 2014 at 18:08
  • \$\begingroup\$ @ptwales, that was all in the same Sub, your subs and functions don't have that much in them that they need to be separated to distinguish between steps. I would space between the functions and subs though, that makes sense. \$\endgroup\$ Commented Aug 15, 2014 at 18:21
  • 3
    \$\begingroup\$ I would definitely class it under personal preference. \$\endgroup\$ Commented Dec 18, 2015 at 14:47

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.