7 ineffective coding habits many f# programmers don't have

Post on 05-Jan-2017

91.734 Views

Category:

Engineering

8 Downloads

Preview:

Click to see full reader

TRANSCRIPT

7 ineffectivecodinghabitsMANYF#programmersDON’Thave

BuildStuff ‘14

habitˈhabɪt/

A settled or regular tendency or practice, especially one that is hard to give up.

“I’m not a great programmer; I’m just a good programmer

with great habits.”

- Kent Beck

Noisy CodeVisual DishonestyLego NamingUnderabstractionUnencapsulated StateGetters and SettersUncohesive Tests

@theburningmonk

does the language I use make a difference?

“Programming languages have a devious influence: they shape our thinking

habits.”

- Edsger W. Dijkstra

Noisy Code

@theburningmonk

@theburningmonk

@theburningmonk

every LOC is a cost

@theburningmonk

more code

more chance for bugs

@theburningmonk

more code

more engineers

@theburningmonk

@theburningmonk

You should do whatever possible to increase the productivity of individual programmers in terms of the expressive power of the code they write. Less code to do the same thing (and possibly better). Less programmers to hire. Less organizational communication costs.

@theburningmonk

You should do whatever possible to increase the productivity of individual programmers in terms of the expressive power of the code they write. Less code to do the same thing (and possibly better). Less programmers to hire. Less organizational communication costs.

does the language I use make a difference?

@theburningmonk

@theburningmonksource http://bit.ly/1oBHHh1

@theburningmonksource http://bit.ly/1oBHHh1

@theburningmonksource http://bit.ly/1oBHHh1

@theburningmonksource http://bit.ly/1oBHHh1

@theburningmonksource http://bit.ly/1oBHHh1

@theburningmonksource http://bit.ly/1oBHHh1

@theburningmonksource http://bit.ly/1oBHHh1

@theburningmonk

Recap

@theburningmonk

no { }no nulls

fewer syntactic noise

@theburningmonk

fewer code

fewer noise

@theburningmonk

fewer noise

higher SNR

@theburningmonk

fewer code

more productivity

- Dan North

“Lead time to someone saying thank you is the only reputation

metric that matters.”

Visual Dishonesty

“…a clean design is one that supports visual thinking so

people can meet their informational needs with a

minimum of conscious effort.”

- Daniel Higginbotham (www.visualmess.com)

@theburningmonk

public void MyCleverMethod( int firstArg, string secondArg)

signifies hierarchy

“You convey information by the way you arrange a design’s elements in relation to each other. This information is understood

immediately, if not consciously, by the people viewing your designs.”

- Daniel Higginbotham (www.visualmess.com)

“This is great if the visual relationships are obvious and accurate, but if they’re not, your audience is going to get confused.

They’ll have to examine your work carefully, going back and forth between the different

parts to make sure they understand.”

- Daniel Higginbotham (www.visualmess.com)

@theburningmonk

Whilst talking with an ex-colleague, a question came up on how to implement the Stable Marriage problem using a message passing approach. Naturally, I wanted to answer that question with Erlang!

Let’s first dissect the problem and decide what processes we need and how they need to interact with one another.

The stable marriage problem is commonly stated as:Given n men and n women, where each person has ranked all members of the opposite sex with a unique number between 1 and n in order of preference, marry the men and women together such that there are no two people of opposite sex who would both rather have each other than their current partners. If there are no such people, all the marriages are “stable”. (It is assumed that the participants are binary gendered and that marriages are not same-sex).From the problem description, we can see that we need:* a module for man* a module for woman* a module for orchestrating the experimentIn terms of interaction between the different modules, I imagined something along the lines of…

how we read ENGLISH

see also http://bit.ly/1KN8cd0

@theburningmonk

Whilst talking with an ex-colleague, a question came up on how to implement the Stable Marriage problem using a message passing approach. Naturally, I wanted to answer that question with Erlang!

Let’s first dissect the problem and decide what processes we need and how they need to interact with one another.

The stable marriage problem is commonly stated as:Given n men and n women, where each person has ranked all members of the opposite sex with a unique number between 1 and n in order of preference, marry the men and women together such that there are no two people of opposite sex who would both rather have each other than their current partners. If there are no such people, all the marriages are “stable”. (It is assumed that the participants are binary gendered and that marriages are not same-sex).From the problem description, we can see that we need:* a module for man* a module for woman* a module for orchestrating the experimentIn terms of interaction between the different modules, I imagined something along the lines of…

2. top-to-bottom1.left-to-right

how we read ENGLISH

see also http://bit.ly/1KN8cd0

@theburningmonk

how we read CODE

public void DoSomething(int x, int y){ Foo(y, Bar(x, Zoo(Monkey())));}

see also http://bit.ly/1KN8cd0

@theburningmonk

how we read CODE

public void DoSomething(int x, int y){ Foo(y, Bar(x, Zoo(Monkey())));}

2. bottom-to-top

1.right-to-left

see also http://bit.ly/1KN8cd0

@theburningmonk

Whilst talking with an ex-colleague, a question came up on how to implement the Stable Marriage problem using a message passing approach. Naturally, I wanted to answer that question with Erlang!

Let’s first dissect the problem and decide what processes we need and how they need to interact with one another.

The stable marriage problem is commonly stated as:Given n men and n women, where each person has ranked all members of the opposite sex with a unique number between 1 and n in order of preference, marry the men and women together such that there are no two people of opposite sex who would both rather have each other than their current partners. If there are no such people, all the marriages are “stable”. (It is assumed that the participants are binary gendered and that marriages are not same-sex).From the problem description, we can see that we need:* a module for man* a module for woman* a module for orchestrating the experimentIn terms of interaction between the different modules, I imagined something along the lines of…

2. top-to-bottom

1.left-to-right

how we read ENGLISH

public void DoSomething(int x, int y){ Foo(y, Bar(x, Zoo(Monkey())));}

2. top-to-bottom

1.right-to-left

how we read CODE

see also http://bit.ly/1KN8cd0

@theburningmonk

|>see also http://bit.ly/1KN8cd0

@theburningmonk

how we read CODE

let drawCircle x y radius = circle radius |> filled (rgb 150 170 150) |> alpha 0.5 |> move (x, y)

see also http://bit.ly/1KN8cd0

@theburningmonk

how we read CODE

let drawCircle x y radius = circle radius |> filled (rgb 150 170 150) |> alpha 0.5 |> move (x, y)

2. top-to-bottom1.left-to-right

see also http://bit.ly/1KN8cd0

@theburningmonk

{}

@theburningmonk

public ResultType MyCleverMethod( int firstArg, string secondArg, string thirdArg) { var localVar = AnotherCleverMethod(firstArg, secondArg); if (localVar.IsSomething( thirdArg, MY_CONSTANT)) { DoSomething(localVar); } return localVar.GetSomething();}

@theburningmonk

XXXXXX XXXXXXXXXX XXXXXXXXXXXXXX XXX XXXXXXXX XXXXXX XXXXXXXXX XXXXXX XXXXXXXX XXX XXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXX XXXXXXXXX XX XXXXXXXX XXXXXXXXXXX XXXXXXXX XXXXXXXXXX XXXXXXXXXXX XXXXXXXX XXXXXX XXXXXXXX XXXXXXXXXXXX

@theburningmonk

public ResultType MyCleverMethod( int firstArg, string secondArg, string thirdArg) { var localVar = AnotherCleverMethod(firstArg, secondArg); if (localVar.IsSomething( thirdArg, MY_CONSTANT)) { DoSomething(localVar); } return localVar.GetSomething();}

“This is great if the visual relationships are obvious and accurate, but if they’re not, your audience is going to get confused.

They’ll have to examine your work carefully, going back and forth between the different

parts to make sure they understand.”

@theburningmonk

public ResultType MyCleverMethod( int firstArg, string secondArg, string thirdArg) { var localVar = AnotherCleverMethod(firstArg, secondArg); if (localVar.IsSomething( thirdArg, MY_CONSTANT)) { DoSomething(localVar); } return localVar.GetSomething();}

@theburningmonk

XXXXXX XXXXXXXXXX XXXXXXXXXXXXXX XXX XXXXXXXX XXXXXX XXXXXXXXX XXXXXX XXXXXXXX

XXX XXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXX XXXXXXXXX XX XXXXXXXX XXXXXXXXXXX XXXXXXXX XXXXXXXXXX

XXXXXXXXXXX XXXXXXXX XXXXXX XXXXXXXX XXXXXXXXXXXX

- Douglas Crockford

“It turns out that style matters in programming for

the same reason that it matters in writing.

It makes for better reading.”

@theburningmonk

two competing rules for structuring code in C-style languages

@theburningmonk

Compiler

{ }

Human

{ } + whitespace

@theburningmonk

what if…?

@theburningmonk

Compiler

whitespace

Human

whitespace

@theburningmonk

xxx {}

xxx {}

no braces no

problem

@theburningmonk

There should be one - and preferably only one - obvious way to do it.

- the Zen of Python

@theburningmonk

let myCleverFunction x y z = let localVar = anotherCleverFunction x y

if localVar.IsSomething(z, MY_CONSTANT) then doSomething localVar localVar.GetSomething()

@theburningmonk

XXX XXXXXXXXXXXXXXXX X X X XXX XXXXXXXX XXXXXXXXXXXXXXXXXXXX X X

XX XXXXXXXX XXXXXXXXXXX X XXXXXXXXXX XXXX XXXXXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXXXXXX

@theburningmonk

You should do whatever possible to increase the productivity of individual programmers in terms of the expressive power of the code they write. Less code to do the same thing (and possibly better). Less programmers to hire. Less organizational communication costs.

@theburningmonk

Recap

@theburningmonk

|>

@theburningmonk

one way to describe hierarchy

Lego Naming

@theburningmonk

naming is HARD

- Phil Karlton

“There are only two hard things in Computer Science: cache

invalidation and naming things.”

- Mike Mahemoff

“Names are the one and only tool you have to explain what a variable does in every place it

appears, without having to scatter comments everywhere.”

@theburningmonk

Lego NamingGluing common words together in an

attempt to create meaning.

@theburningmonk

Strategy

Process

Create

Add

Controller

Factory

Proxy

ObjectException

Enable

Do

Disable

Service

RemoveCheck

GetSet

Update

Validate

@theburningmonk

see http://methodnamer.com

@theburningmonk

this is not naming

@theburningmonk

this is not namingthis is labelling

@theburningmonk

@theburningmonk

@theburningmonk

naming is HARD

@theburningmonk

anonymous functionsaka lambdas

@theburningmonk

fewer things to name

@theburningmonk

words |> Array.map (fun x -> x.Count) |> Array.reduce (+)

@theburningmonk

smaller scopeshorter names

@theburningmonk

@theburningmonk

http://bit.ly/1ZpAByu

When x, y, and z are great variable names

@theburningmonk

"The length of a name should be related to the length of the scope. You can use very short variable names for tiny scopes, but for big

scopes you should use longer names.

Variable names like i and j are just fine if their scope is five lines long."

- Robert C. Martin

@theburningmonk

object expressions

@theburningmonk

enterpriseCrew.OrderBy( (fun c -> c.Current), { new IComparer<Occupation> with

member this.Compare(x, y) = x.Position.CompareTo(y.Position) })

@theburningmonk

enterpriseCrew.OrderBy( (fun c -> c.Current), { new IComparer<Occupation> with

member this.Compare(x, y) = x.Position.CompareTo(y.Position) })

@theburningmonk

fewer things to name

@theburningmonk

tuples + pattern matching

@theburningmonk

tuples + pattern matching

fewer abstractions

@theburningmonk

tuples + pattern matching

fewer abstractions

fewer things to name

@theburningmonk

words |> Seq.groupBy id |> Seq.map (fun (word, gr) -> word, Seq.length gr) |> Seq.iter (fun (word, len) -> printfn “%s - %s” word len)

@theburningmonk

words |> Seq.groupBy id |> Seq.map (fun (word, gr) -> word, Seq.length gr) |> Seq.iter (fun (word, len) -> printfn “%s - %s” word len)

@theburningmonk

words |> Seq.groupBy id |> Seq.map (fun (word, gr) -> word, Seq.length gr) |> Seq.iter (fun (word, len) -> printfn “%s - %s” word len)

@theburningmonk

words |> Seq.groupBy id |> Seq.map (fun (word, gr) -> word, Seq.length gr) |> Seq.iter (fun (word, len) -> printfn “%s - %s” word len)

@theburningmonk

Lego Naming can also be the symptom of a failure to

identify the right level of abstractions.

@theburningmonk

the RIGHT level of abstraction might be smaller than “object”

@theburningmonk

public interface ConditionChecker{ bool CheckCondition();}

@theburningmonk

public interface Condition{ bool IsTrue();}

@theburningmonk

@theburningmonk

type Condition = unit -> bool

source https://vimeo.com/113588389

@theburningmonk

ClassNotFoundException IllegalArgumentException IndexOutOfBoundsException NoSuchMethodException UnsupportedOperationException

@theburningmonk

ClassNotFound IllegalArgument IndexOutOfBounds NoSuchMethod UnsupportedOperation

@theburningmonk

ArithmeticException ArrayStoreException ClassCastException InstantiationException NullPointerException SecurityException

@theburningmonk

IntegerDivisionByZero IllegalArrayElementType CastToNonSubclass ClassCannotBeInstantiated NullDereferenced SecurityViolation

@theburningmonk

lightweight exception syntax

@theburningmonk

open System open System.IO

exception InsufficientBytes

@theburningmonk

open System open System.IO

exception InsufficientBytes

what could this type represent?

@theburningmonk

Recap

@theburningmonk

F# < > silver bullet

@theburningmonk

anonymous functions

fewer things to name

@theburningmonk

short names

@theburningmonk

tuple + pattern matching

fewer things to name

@theburningmonk

no abstraction is too small

@theburningmonk

lightweight exception syntax

Underabstraction

@theburningmonk

@theburningmonk

public Result DoSomething( int a, string b, string c, string d, DateTime e, DateTime f, string g, MyEnum h)

“If you have a procedure with ten parameters, you probably missed some.”

- Alan Perlis

source https://vimeo.com/97507575

@theburningmonk

lightweight syntax for types and hierarchies

@theburningmonk

record

@theburningmonk

type Employee = { FirstName : string Surname : string Salary : int<Pound> }

@theburningmonk

type Employee = { FirstName : string Surname : string Salary : int<Pound> } immutable by default

@theburningmonk

let promote emp raise = { emp with Salary <- emp.Salary + raise }

@theburningmonk

mutable state complects

value and time

@theburningmonk

type Employee = { FirstName : string Surname : string Salary : int<Pound> } unit-of-measure

@theburningmonk

[<Measure>]type Pound

e.g. 42<Pound> 153<Pound>

10<Meter> / 2<Second> = 5<Meter/Second>10<Meter> * 2<Second> = 20<Meter Second> 10<Meter> + 10<Meter> = 20<Meter>10<Meter> * 10 = 100<Meter>10<Meter> * 10<Meter> = 100<Meter2>10<Meter> + 2<Second> // error10<Meter> + 2 // error

10<Meter> / 2<Second> = 5<Meter/Second>10<Meter> * 2<Second> = 20<Meter Second> 10<Meter> + 10<Meter> = 20<Meter>10<Meter> * 10 = 100<Meter>10<Meter> * 10<Meter> = 100<Meter2>10<Meter> + 2<Second> // error10<Meter> + 2 // error

@theburningmonk

discriminated unions

@theburningmonk

type PaymentMethod = | Cash | Cheque of ChequeNumber | Card of CardType * CardNumber

Unencapsulated State

@theburningmonk

@theburningmonk

public class RecentlyUsedList { private List<string> items = new List<string>(); public List<string> Items { get { return items; } }

… }

@theburningmonk

immutability

@theburningmonk

type RecentlyUsedList (?items) = let items = defaultArg items [ ]

member this.Items = Array.ofList items

member this.Count = List.length items

member this.Add newItem = newItem::(items |> List.filter ((<>) newItem)) |> RecentlyUsedList

@theburningmonk

Affordancean affordance is a quality of an object, or

an environment, which allows an individual to perform an action. For example, a knob

affords twisting, and perhaps pushing, whilst a cord affords pulling.

source https://www.youtube.com/watch?v=aAb7hSCtvGw

@theburningmonk

your abstractions should afford right behaviour,

whilst make it impossible to do the wrong thing

@theburningmonk

“Make illegal states unrepresentable”

- Yaron Minsky

@theburningmonk

discriminated unions

@theburningmonk

type PaymentMethod = | Cash | Cheque of ChequeNumber | Card of CardType * CardNumber

finite, closed set of valid states ONLY

closed hierarchy

no Nulls

@theburningmonk

match paymentMethod with | Cash -> … | Cheque chequeNum -> … | Card (cardType, cardNum) -> …

@theburningmonk

Recap

@theburningmonk

immutability

@theburningmonk

make illegal state unrepresentable

Getters and Setters

“When it’s not necessary to change, it’s necessary to not change.”

- Lucius Cary

“Now we have shortcuts to do the wrong thing.

We used to have type lots to do the wrong thing, not anymore.”

- Kevlin Henney

@theburningmonk

immutability by default

@theburningmonk

type Person = { Name : string Age : int }

@theburningmonk

type Person = { mutable Name : string mutable Age : int }

@theburningmonk

immutability

Uncohesive Tests

@theburningmonk

MethodA

MethodB

When_…Then_… ()When_…Then_… ()When_…Then_… ()When_…Then_… ()When_…Then_… ()When_…Then_… ()

@theburningmonk

MethodA

MethodB

MethodC

FeatureA

FeatureB

@theburningmonk

complexities & potential bugs in the way methods

work together

@theburningmonk

…especially when states are concerned

@theburningmonk

Test Driven Development

“For tests to drive development they must do more than just test that code

performs its required functionality: they must clearly express that required

functionality to the reader. That is, they must be clear specification of the

required functionality.”

- Nat Pryce & Steve Freeman

@theburningmonk

@theburningmonk

how many tests?

@theburningmonk

every test has a cost

@theburningmonk

did we cover all the edge cases?

@theburningmonk

Property-Based Testing(with FsCheck)

@theburningmonk

List.rev

reverse + reverse = originallength of list is invariantappend + reverse = reverse + prepend

@theburningmonk

List.revproperty : reverse + reverse = original

let ``reverse + reverse = original`` rev aList = aList |> rev |> rev = aList

Check.Quick (``reverse + reverse = original`` List.rev) // Ok, passed 100 tests.

@theburningmonk

List.revproperty : length of list is invariant

let ``length of list is invariant`` rev aList = List.length (rev aList) = List.length aList

Check.Quick (``length of list is invariant`` List.rev) // Ok, passed 100 tests.

@theburningmonk

List.revproperty : append + reverse = reverse + prepend

let ``append + reverse = reverse + prepend`` rev x aList = (aList @ [x]) |> rev = x::(aList |> rev)

Check.Quick (``append + reverse = reverse + prepend`` List.rev) // Ok, passed 100 tests.

@theburningmonk

Check.Verbose (``append + reverse = reverse + prepend`` List.rev) // 0: ‘\005' [] 1: false ["N "] 2: “" [false; '{'] 3: ‘\017' [true; true; 'W'] 4: “" [""; false] 5: “yg]" [“H\nOq6"; null; false; false; '#'] 6: true [“"] … 11: <null> ['\014'; '0'; “\nRH”; "<#oe"; true; false; ‘O'] …

@theburningmonk

shrinking

@theburningmonk

Check.Quick (``append + reverse = reverse + prepend`` id) // Falsifiable, after 2 tests (4 shrinks) (StdGen (1855582125,296080469)):

Original:‘\013' ["}k"; ""; “"]

Shrunk:true [false]

@theburningmonk

let computers do the grunt work

source : http://bit.ly/1kEpEso

@theburningmonk

Types vs Tests

@theburningmonk

all bugs

@theburningmonk

unknown

known

@theburningmonk

tests

types

@theburningmonk

tests

types

@theburningmonk

unit-testing

distr. systemssystem-testing

@theburningmonk

Jepsenproperty-based

unit-testing system-testing

distr. systems

@theburningmonk

Jepsenproperty-based

unit-testing

types as proof TLA+

distr. systemssystem-testing

Noisy CodeVisual DishonestyLego NamingUnderabstractionUnencapsulated StateGetters and SettersUncohesive Tests

“Practice does not make perfect.Only perfect practice makes perfect.”

- Vince Lombardi

“Perfection is not attainable. But if we chase perfection, we can catch excellence.”

- Vince Lombardi

“Programming languages have a devious influence: they shape our thinking

habits.”

- Edsger W. Dijkstra

“One of the most disastrous thing we can learn is the first programming language, even

if it's a good programming language.”

- Alan Kay

“I’m not a great programmer; I’m just a good programmer

with great habits.”

- Kent Beck

@theburningmonk

what about ineffective coding habits SOME F#/FP

programmers DO have?

@theburningmonk

@theburningmonk

people are too puritanical about purity

…premature optimization is the root of all evil. Yet we should not pass up our opportunities in that

critical 3%

- Donald Knuth

@theburningmonk

F# Map vs .Net array vs Dictionary

@theburningmonk

@theburningmonk

Explicit is better than implicit.

- the Zen of Python

@theburningmonk

Simple is better than Complex.Complex is better than Complicated.

- the Zen of Python

@theburningmonk

Special cases aren't special enough to break the rules.

- the Zen of Python

@theburningmonk

Special cases aren't special enough to break the rules.

Although practicality beats purity.

- the Zen of Python

@theburningmonk

If the implementation is hard to explain, it's a bad idea.

- the Zen of Python

@theburningmonk

@theburningmonktheburningmonk.comgithub.com/theburningmonk

@theburningmonk

is hiring :-)http://tech.just-eat.com/jobs

top related