views:

294

answers:

4

What are some of the common signs your taking the wrong approach in a mixed functional / imperative / OO environment?

+1  A: 

Capturing a mutable in a sequence.

You can do the same thing in languages like C# but I consider a sequence (something lazy) as a functional construct. This should print "1 2 " twice but only prints it once.

let lessThan y (x : seq<'a>) = //'
    let xe = x.GetEnumerator()
    seq {
        while xe.MoveNext() && xe.Current < y do
            yield xe.Current
    }

let result = seq {1..5} |> lessThan 3
result |> Seq.iter (fun i -> printf "%d " i)
result |> Seq.iter (fun i -> printf "%d " i)
gradbot
I might broaden this to "mixing lazy evaluation with side-effects".
Brian
Alternatively, I might broaden this in a different direction to say that "calling GetEnumerator() is a code smell".
Brian
+8  A: 

Java with ML syntax

A lot of newbies, especially those who come from C# and Java, end up writing code their OCaml/F# code with Java-like idioms:

type 'a FruitVisitor =
    abstract Visit : Apple -> 'a
    abstract Visit : Orange -> 'a
    abstract Visit : Pear -> 'a

and [<AbstractClass>] Fruit() =
    abstract Accept : 'a FruitVisitor -> 'a

and Apple() =
    inherit Fruit()
    override this.Accept(visitor) = visitor.Visit(this)

and Orange() =
    inherit Fruit()
    override this.Accept(visitor) = visitor.Visit(this)

and Pear() =
    inherit Fruit()
    override this.Accept(visitor) = visitor.Visit(this)

let FruitCaloriesVisitor() =
    {
        new FruitVisitor<int> with
            member this.Visit(fruit : Apple) = 65
            member this.Visit(fruit : Orange) = 110
            member this.Visit(fruit : Pear) = 80
    }

let fruitsAndCalories =
    let visitor = FruitCaloriesVisitor()

    [new Apple() :> Fruit; upcast new Orange(); upcast new Pear()]
    |> List.map (fun x -> x, x.Accept(visitor))

Mind you, visitor is one of my favorite patterns, and as much I love writing the dozens of lines of boilerplate code over and over and over, I'd much rather write this instead:

type fruit = Apple | Orange | Pear

let calories = function
    | Apple -> 65
    | Orange -> 110
    | Pear -> 80

let fruitsAndCalories =
    [ Apple; Orange; Pear ] |> List.map (fun x -> x, calories x)
Juliet
Thanks for the answer Juliet. I'm a bit sad I only got two replies.
gradbot
+3  A: 

A big one for mixed functional/imperative code is not keeping the imperative code on the "outside" as much as possible. Functional-oriented idioms such as lazy evaluation, partial application, and closures can start having... strange issues in the presence of mutable state and side effects. Imperative code should be calling into functional-style code, not the other way around.

In Haskell, the type system generally enforces the separation. In most other languages, you're on your own...

camccann
+1. In D, functions can be annotated as `pure` and pure functions cannot call impure functions, so the compiler enforces that functional-style code can't call imperative code.
dsimcha
+4  A: 

Getting cute with pointless (point-free) code

This one generalizes to functional programming as a whole, not just impure languages, but a while ago I came across this snippet of F#:

> let flip f y x = f x y
- [1..10]
-   |> List.map((*) 2)
-   |> List.filter(flip (%) 3 >> (=) 0)
-   |> List.iter(printfn "%d");;
6
12
18

$100 says you can't guess which line gives me the 'splody eyes ;)

Alright, more seriously, I can't think of any reason to prefer List.filter(flip (%) 3 >> (=) 0) over List.filter (fun n -> n % 3 = 0).

However, some point-free code, like List.iter(printfn "%d") and List.reduce (+) are a little more idiomatic than others.

So when should you use pointful vs point-free style? Its depends -- sometimes point-free style can tidy up code, sometimes it requires the same mental gymnastics needed to write a novel without the letter 'e'.

In my experience, point-free code is very brittle and resists refactoring, since flipping or adding more arguments in a function can drastically alter its point-free expansion. See the following Haskell:

\x -> x * x         == join (*)
\x -> x * x * x     == (*) =<< join (*)

When in doubt, default to pointful style.

Juliet