7 ineffective coding habits many f# programmers don't have
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
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
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