I have a stream parsing method in C# that reads portions of a protocol frame from the STOMP protocol; the specification isn't the important part for the question though. What is important is that I have written the code in an imperative style, since that is what I know.

Can anyone give some examples of processing in a functional manner? (Psuedo-code, or any language will constitute an acceptable answer)

/// <summary>
/// Reads a STOMP command string asynchronyously.
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns>CommandString if read; otherwise null</returns>
public async Task<string> ReadStompCommand(CancellationToken cancellationToken)
{
    string commandString = null;

    //Create a new buffer, maximum possible allowed bytes in the line is 13
    var commandBuffer = new byte[13]; 

    //EOL is the line ending as defined in the spec of STOMP 1.2
    //EOL can either be CRLF or LF alone
    var eolIndex = -1;
    var bytesRead = 0;

    //while we haven't maxed out our buffer and we haven't found an EOL sequence
    while (bytesRead < commandBuffer.Length && eolIndex == -1)
    {
        //move forward in the buffer
        var offset = bytesRead;

        //Reading 1 byte at a time for now, may change in a future implementation
        const int length = 1;
        var bytesFound = 0;

        bytesFound += await Stream.ReadAsync(commandBuffer, offset, length, cancellationToken);

        //check for EOL in the bytes we read. (1 iteration since length is const of 1)
        for (var i = offset; i < offset + bytesFound; i++)
        {
            if (commandBuffer[i] == 0x0a)
                eolIndex = i;
        }

        bytesRead += bytesFound;
    }

    //if we have a potentially meaningful line (STOMPs shortest command is 3 characters)
    if (eolIndex > 2)
    {
        //If last byte before LF was CR, move the EOL start index back one
        if (commandBuffer[eolIndex - 1] == 0x0d)
        {
            eolIndex--;
        }

        //Convert bytes to string in UTF-8 from beginning to start of EOL
        commandString = Encoding.UTF8.GetString(commandBuffer, 0, eolIndex);
    }

    return commandString;
}
有帮助吗?

解决方案

I have a stream parsing method in C# that reads portions of a protocol frame from the STOMP protocol... Can anyone give some examples of processing in a functional manner?

You're thinking about this at the wrong level.

The interesting question is not "how would I implement this low-level byte-buffer code in a functional style?" The private implementation details of the method are just that -- private implementation details. There is no benefit to the user of the method of your writing the method's implementation in a functional style.

The interesting question is: how can I make the method as a whole more amenable to users of the method who wish to program in a functional style?

Because your method here is really not functional. It takes no arguments (of any interest; plainly the value of the cancellation token does not influence the output), and it returns a deferred string that will potentially be different every time it is fetched. Or, more accurately, it returns a deferred string that will get passed to a callback when it is available.

What then is the more functional way to structure this code? We wish to think of the entire sequence of strings that will be pushed into callbacks going into the future as a single object. Just the same way that we think of an on-demand sequence of elements -- IEnumerable<T> -- as being a single object that can provide arbitrarily many objects in the future.

In short, the more functional way to structure this code is by creating an object that is an observable collection of strings. The thing you want to research here is functional reactive programming.

That should be enough to get you started down this particular rabbit hole.

Bonus: once you have a basic understanding of FRP, try wrapping your head around the IO monad in Haskell if you really want to see how to structure these sorts of operations in a purely functional style.

其他提示

For your specific piece of code, it's very low level, reading bytes from an input and writing them to an output. In C#, this code is not going to be functional.

But you can add a wrapper so that you can pass a fake "state of the world" to your non-functional methods and make them return the "altered" state of the world along with your result.

The signature is:

class World {}
class WorldAndResult<T> {
    World world;
    T t; // this is your normal result
    WorldAndResult(World w, T theT) { world = w; t = theT; }
}

public WorldAndResult<Int> mutator(World w, otherArgs...) {
    int ret = wrappedMutator(otherArgs...);
    return new WorldAndResult(w, ret);
}

Now you can chain these calls together and pretend they are functional and that they mutate the "World". It's a dummy object that stands in for whatever side effects your method has.

final WorldAndResult<Integer> war = mutator(new World(), 37);
final WorldAndResult<Integer> war2 = mutator(war.world, 54);

Computer hardware is imperative. It mutates in place. It has side effects: hardware interrupts, writing to disk, changing the display buffer, reading and writing from the network card...

Every functional language, every functional tool-kit, wraps all the imperative stuff in a minimum-side-effect wrapper that prevents non-essential mutating in place. When necessary for performance reasons, it does mutate in place, but it hides that mutation from the programmer.

Functional programming is efficient in the large, even if it can be inefficient for some details. Simplifying your code frees your brain up to make more efficient design choices that you would when you concentrate on bit twiddling and looping. So I always start functional. It is very rare that I have to take a second pass to speed something up. Unless, of course, I'm writing infrastructure to use in functional programming. :-)

许可以下: CC-BY-SA归因
scroll top