9
\$\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
\$\endgroup\$
10
\$\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.
\$\endgroup\$
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.

\$\endgroup\$
  • 3
    \$\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\$ – cheezsteak Aug 15 '14 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\$ – Malachi Aug 15 '14 at 18:21
  • 1
    \$\begingroup\$ I would definitely class it under personal preference. \$\endgroup\$ – Kaz Dec 18 '15 at 14:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.