
A Gray Beard Explores F# - mperkins
http://graybeardprogrammer.com/?p=1
======
chubot
"The avoidance of side-effects is one of the key objectives of functional
programming. Advocates of functional programming claim a number of benefits
for the absence of side-effects including a greater ease of reading and
reasoning about the code and an increased ability to execute parts of a
computation in parallel."

You know I'm sympathetic to this line of thinking, but I wished he would have
actually used this VM example to back up these statements.

Why is the version without mutable state better? That's not explained at all.
I can think of some reasons why it would be worse -- e.g. more memory
allocations. For this VM, its semantics are inherently serial, so the
concurrency argument doesn't apply.

~~~
mquander
Well, the second version here is better for the following reasons:

\- 9 less lines of code.

\- Easier to see that it's correct. Partly because it's less lines, but partly
because those lines are more meaningful and less bland. It would be hard to
see a typo in the repetitive inner loop of the first version.

\- Two obvious independent pieces of code instead of one big piece of code.
That means if you're reading it, you know how to go about understanding it;
understand each piece in turn. Somewhat trivial when there are 25 lines, but
serious if there are 200 lines.

\- Easier to test and debug. At a REPL, it's trivial to see what the stack is
going to look like five hops into the functional version; you just apply the
processing function five times. To do the same to the imperative version, you
need to either write some debug logic into the middle of the function or set a
breakpoint and hit 'continue' in your debugger while you count on your
fingers.

Those reasons are typical consequences of writing your code as a collection of
data and pure functions.

Also, the semantics are _not_ completely inherently serial for most possible
stacks. The sequence of operations forms a tree of computation, and the
branches of the tree could be computed in parallel. If you actually wanted to
do that (say, if the operations took a long time individually) then the way
you would go about it is to construct the tree explicitly up front, take this
functional version, replace fold with a version of fold that knows how to fold
over trees in parallel, and fold it over the tree. Ta-da -- it would just
work.

~~~
Daniel_Newby
Another reason the second may be better:

\- Wrap the stack program in a transaction and simply discard the results to
roll back the transaction. In an imperative program it is error prone to roll
back a complicated operation—the most innocent looking function might mutate
external state.

~~~
masklinn
Well technically F# has no purity handling, so it's just as possible for the
most innocent-looking F# function to have side-effects as well.

~~~
Daniel_Newby
Thanks for the warning. That sucks.

------
jamesaguilar
I don't have much commentary except to say that that was a pleasure to read
and I hope we can see more like this in the future.

~~~
ryanbraganza
Was there a particular part you found especially pleasurable? What made it
more pleasurable for you to read than other writing?

~~~
jamesaguilar
Good question. The thing I most loved was the progressive refinement of the
technique. I love seeing things transformed from rough to fine and seeing the
rationale for each step.

------
kevingadd
It's neat to see how much more expressive F# manages to be compared to C#. One
of his examples (passing an operator as an argument to a function) would
probably take dozens or hundreds of lines to express in C# because it's just
not something that's built into the language.

~~~
plorkyeran
Er, the C# equivalent of applyBinaryOp state (+) is just applyBinaryOp(state,
(a, b) => a + b).

~~~
kevingadd
That's not an operator, that's a lambda. The end result is the same, but
semantically it's very different. Passing around an operator means you don't
need to know the types involved - you can have a function that operates on any
two values and applies an operator to them. That's not something you can do
with lambdas in C#.

~~~
mquander
No, there's no difference whatsoever. .NET has no concept of "operators" that
are different from functions.

F# is statically typed, just like C#, and the only reason you can pass + in
this case is because the type of the stack is Stack<int>, so the compiler can
statically infer that "+" means "the + operator on ints." At every usage, +
has to refer to a specific function, or it wouldn't compile. For example, if
it was a Stack<object> that happened to contain ints, it wouldn't compile.

(You could use .NET's "dynamic" type to make it dynamically resolve which
function at runtime, but you could use it equally well in the C# example with
a lambda.)

~~~
SideburnsOfDoom
> .NET has no concept of "operators" that are different from functions

That doesn't sound right. I'm pretty sure that arithmetic "+" is not a
function. Citation needed.

~~~
masklinn
There might be some optimizations on top of that, but overloadable operators
in C# do get compiled to a function call (a static method call, if you want to
be pedantic): [http://stackoverflow.com/questions/6587107/c-sharp-
operator-...](http://stackoverflow.com/questions/6587107/c-sharp-operator-
overload-for)

See VMAtm's comment's bytecode, showing that "[Decimal] + [Decimal]" compiles
to:

    
    
        IL_0011: call    valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Addition(valuetype [mscorlib]System.Decimal,
                                                                                                  valuetype [mscorlib]System.Decimal)
    

Non-overloadable operators (short-circuiting operators (|| and &&), ternaries
and its ilk, field access and (of course) the assignment operator) are
probably compiled straight to bytecode, but they're pretty special and
(especially in a strict language) need special runtime support anyway[0].

To sum up: mquander is completely right, the CLR does not know anything about
operators, language compilers will take whatever operator they have at a high
level and compile them to method calls (usually). For more details, see
<http://www.amazon.com/dp/0735627045/>

[0] other runtimes make different choices, Python encodes most of its
operators into bytecode for instance:
<http://docs.python.org/library/dis.html#opcode-BINARY_POWER>

~~~
SideburnsOfDoom
Interesting. However, operators and functions present very differently when
writing c# code.

It's easy to put a lambda or a named function in a variable, pass it to a
function, etc. However, if you want to do that to an operator such as "+", the
first thing to do is to wrap it in a lambda - e.g.

    
    
      // invalid
      // Func<int, int, int> plus = + ;
      
      // works in a wrapper fn
      Func<int, int, int> plusFn = (a, b) => a + b;
    
    

Related, if you have a function which is generic for some type T, you can call
functions of type Func<T> on the values in it, but you can't use operators
like + - c# just isn't generic on operators, though it can be on functions.

    
    
      public T ApplySomeFunc<T>(Func<T, T> func, T value)
      {
        // this works
        return func(value);
    
        // this does not
        //return value + value;
      }
    

I know that the language has no way to specify that the type T has a "+"
operator so that's not going to work; but this catches people out regularly,
when they try and fail to make math functions that are generic over all
numeric types. However, you can do something similar with the "dynamic" type,
at the cost or run-time-dispatch.

~~~
masklinn
> Interesting. However, operators and functions present very differently when
> writing c# code.

Aye, but that's more of a limitation of C#'s syntax

> However, if you want to do that to an operator such as "+", the first thing
> to do is to wrap it in a lambda - e.g.

That's pretty much what a section (the parens around the operators) does in F#
or Haskell.

> I know that the language has no way to specify that the type T has a "+"
> operator so that's not going to work

Well it's not so much that "operators and functions present very differently
when writing C# code" but that "methods and functions present very differently
when writing C# code" and that operators are (static) methods more than
functions in C#.

Also that C# has no numeric tower[0] so there's indeed no way to say a type is
a generic number (whether that number's an int, a double or a decimal). If
there was a root `Number` type you could write something along the lines of:

    
    
        public T AddNumbers<T> where T:Number(T value)
        {
            // works because all numbers have a `+`
            return value + value;
        }
    

which is exactly what you write in Haskell:

    
    
        addNumbers :: (Num a) => a -> a -> a
        addNumbers a b = a + b
    

[0] Haskell has a pretty complete/complex one, starting from Num[1] which
implements only a few basic operations: (+), (-), (*), negation and conversion
from integer.

[1]
[http://hackage.haskell.org/packages/archive/base/latest/doc/...](http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#t:Num)

