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

118.576 visualizações

Publicada em

At BuildStuff'14, Kevlin Henney presented an excellent talk titled "Seven ineffective coding habits of many programmers". As an attendee that day and someone who has exhibited many of these habits over the years, I came to realize that using F# has cured me of many of these ineffective habits! In this talk I'll share my thoughts on how the use of F# and functional programming techniques can help form and nurture good habits and give you the perfect practice you need to make perfect.

Publicada em: Engenharia, Tecnologia
4 comentários
140 gostaram
Estatísticas
Notas
Sem downloads
Visualizações
Visualizações totais
118.576
No SlideShare
0
A partir de incorporações
0
Número de incorporações
6.179
Ações
Compartilhamentos
0
Downloads
163
Comentários
4
Gostaram
140
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide

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

  1. 7 ineffective coding habits MANY F# programmers DON’T have
  2. BuildStuff ‘14
  3. habit ˈhabɪt/ A settled or regular tendency or practice, especially one that is hard to give up.
  4. “I’m not a great programmer; I’m just a good programmer with great habits.” - Kent Beck
  5. Noisy Code Visual Dishonesty Lego Naming Underabstraction Unencapsulated State Getters and Setters Uncohesive Tests
  6. @theburningmonk does the language I use make a difference?
  7. “Programming languages have a devious influence: they shape our thinking habits.” - Edsger W. Dijkstra
  8. Noisy Code
  9. @theburningmonk
  10. @theburningmonk
  11. @theburningmonk every LOC is a cost
  12. @theburningmonk more code more chance for bugs
  13. @theburningmonk more code more engineers
  14. @theburningmonk
  15. @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.
  16. @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.
  17. does the language I use make a difference?
  18. @theburningmonk
  19. @theburningmonk source http://bit.ly/1oBHHh1
  20. @theburningmonk source http://bit.ly/1oBHHh1
  21. @theburningmonk source http://bit.ly/1oBHHh1
  22. @theburningmonk source http://bit.ly/1oBHHh1
  23. @theburningmonk source http://bit.ly/1oBHHh1
  24. @theburningmonk source http://bit.ly/1oBHHh1
  25. @theburningmonk source http://bit.ly/1oBHHh1
  26. @theburningmonk Recap
  27. @theburningmonk no { } no nulls fewer syntactic noise
  28. @theburningmonk fewer code fewer noise
  29. @theburningmonk fewer noise higher SNR
  30. @theburningmonk fewer code more productivity
  31. - Dan North “Lead time to someone saying thank you is the only reputation metric that matters.”
  32. Visual Dishonesty
  33. “…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)
  34. @theburningmonk public void MyCleverMethod( int firstArg, string secondArg) signifies hierarchy
  35. “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)
  36. “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)
  37. @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 experiment In terms of interaction between the different modules, I imagined something along the lines of… how we read ENGLISH see also http://bit.ly/1KN8cd0
  38. @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 experiment In 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 see also http://bit.ly/1KN8cd0
  39. @theburningmonk how we read CODE public void DoSomething(int x, int y) { Foo(y, Bar(x, Zoo(Monkey()))); } see also http://bit.ly/1KN8cd0
  40. @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
  41. @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 experiment In 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
  42. @theburningmonk |> see also http://bit.ly/1KN8cd0
  43. @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
  44. @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-bottom 1.left-to-right see also http://bit.ly/1KN8cd0
  45. @theburningmonk {}
  46. @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(); }
  47. @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
  48. @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(); }
  49. “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.”
  50. @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(); }
  51. @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
  52. - Douglas Crockford “It turns out that style matters in programming for the same reason that it matters in writing. It makes for better reading.”
  53. @theburningmonk two competing rules for structuring code in C-style languages
  54. @theburningmonk Compiler { } Human { } + whitespace
  55. @theburningmonk what if…?
  56. @theburningmonk Compiler whitespace Human whitespace
  57. @theburningmonk xxx { } xxx { } no braces no problem
  58. @theburningmonk There should be one - and preferably only one - obvious way to do it. - the Zen of Python
  59. @theburningmonk let myCleverFunction x y z = let localVar = anotherCleverFunction x y if localVar.IsSomething(z, MY_CONSTANT) then doSomething localVar localVar.GetSomething()
  60. @theburningmonk XXX XXXXXXXXXXXXXXXX X X X XXX XXXXXXXX XXXXXXXXXXXXXXXXXXXX X X XX XXXXXXXX XXXXXXXXXXX X XXXXXXXXXX XXXX XXXXXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXXXXXX
  61. @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.
  62. @theburningmonk Recap
  63. @theburningmonk |>
  64. @theburningmonk one way to describe hierarchy
  65. Lego Naming
  66. @theburningmonk naming is HARD
  67. - Phil Karlton “There are only two hard things in Computer Science: cache invalidation and naming things.”
  68. - 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.”
  69. @theburningmonk Lego Naming Gluing common words together in an attempt to create meaning.
  70. @theburningmonk Strategy Process Create Add Controller Factory Proxy Object Exception Enable Do Disable Service Remove Check Get Set Update Validate
  71. @theburningmonk see http://methodnamer.com
  72. @theburningmonk this is not naming
  73. @theburningmonk this is not naming this is labelling
  74. @theburningmonk
  75. @theburningmonk
  76. @theburningmonk naming is HARD
  77. @theburningmonk anonymous functions aka lambdas
  78. @theburningmonk fewer things to name
  79. @theburningmonk words |> Array.map (fun x -> x.Count) |> Array.reduce (+)
  80. @theburningmonk smaller scope shorter names
  81. @theburningmonk
  82. @theburningmonk http://bit.ly/1ZpAByu When x, y, and z are great variable names
  83. @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
  84. @theburningmonk object expressions
  85. @theburningmonk enterpriseCrew.OrderBy( (fun c -> c.Current), { new IComparer<Occupation> with member this.Compare(x, y) = x.Position.CompareTo(y.Position) })
  86. @theburningmonk enterpriseCrew.OrderBy( (fun c -> c.Current), { new IComparer<Occupation> with member this.Compare(x, y) = x.Position.CompareTo(y.Position) })
  87. @theburningmonk fewer things to name
  88. @theburningmonk tuples + pattern matching
  89. @theburningmonk tuples + pattern matching fewer abstractions
  90. @theburningmonk tuples + pattern matching fewer abstractions fewer things to name
  91. @theburningmonk words |> Seq.groupBy id |> Seq.map (fun (word, gr) -> word, Seq.length gr) |> Seq.iter (fun (word, len) -> printfn “%s - %s” word len)
  92. @theburningmonk words |> Seq.groupBy id |> Seq.map (fun (word, gr) -> word, Seq.length gr) |> Seq.iter (fun (word, len) -> printfn “%s - %s” word len)
  93. @theburningmonk words |> Seq.groupBy id |> Seq.map (fun (word, gr) -> word, Seq.length gr) |> Seq.iter (fun (word, len) -> printfn “%s - %s” word len)
  94. @theburningmonk words |> Seq.groupBy id |> Seq.map (fun (word, gr) -> word, Seq.length gr) |> Seq.iter (fun (word, len) -> printfn “%s - %s” word len)
  95. @theburningmonk Lego Naming can also be the symptom of a failure to identify the right level of abstractions.
  96. @theburningmonk the RIGHT level of abstraction might be smaller than “object”
  97. @theburningmonk public interface ConditionChecker { bool CheckCondition(); }
  98. @theburningmonk public interface Condition { bool IsTrue(); }
  99. @theburningmonk
  100. @theburningmonk type Condition = unit -> bool
  101. source https://vimeo.com/113588389
  102. @theburningmonk ClassNotFoundException IllegalArgumentException IndexOutOfBoundsException NoSuchMethodException UnsupportedOperationException
  103. @theburningmonk ClassNotFound IllegalArgument IndexOutOfBounds NoSuchMethod UnsupportedOperation
  104. @theburningmonk ArithmeticException ArrayStoreException ClassCastException InstantiationException NullPointerException SecurityException
  105. @theburningmonk IntegerDivisionByZero IllegalArrayElementType CastToNonSubclass ClassCannotBeInstantiated NullDereferenced SecurityViolation
  106. @theburningmonk lightweight exception syntax
  107. @theburningmonk open System open System.IO exception InsufficientBytes
  108. @theburningmonk open System open System.IO exception InsufficientBytes what could this type represent?
  109. @theburningmonk Recap
  110. @theburningmonk F# < > silver bullet
  111. @theburningmonk anonymous functions fewer things to name
  112. @theburningmonk short names
  113. @theburningmonk tuple + pattern matching fewer things to name
  114. @theburningmonk no abstraction is too small
  115. @theburningmonk lightweight exception syntax
  116. Underabstraction
  117. @theburningmonk
  118. @theburningmonk public Result DoSomething( int a, string b, string c, string d, DateTime e, DateTime f, string g, MyEnum h)
  119. “If you have a procedure with ten parameters, you probably missed some.” - Alan Perlis
  120. source https://vimeo.com/97507575
  121. @theburningmonk lightweight syntax for types and hierarchies
  122. @theburningmonk record
  123. @theburningmonk type Employee = { FirstName : string Surname : string Salary : int<Pound> }
  124. @theburningmonk type Employee = { FirstName : string Surname : string Salary : int<Pound> } immutable by default
  125. @theburningmonk let promote emp raise = { emp with Salary <- emp.Salary + raise }
  126. @theburningmonk mutable state complects value and time
  127. @theburningmonk type Employee = { FirstName : string Surname : string Salary : int<Pound> } unit-of-measure
  128. @theburningmonk [<Measure>] type Pound e.g. 42<Pound> 153<Pound>
  129. 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> // error 10<Meter> + 2 // error
  130. 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> // error 10<Meter> + 2 // error
  131. @theburningmonk discriminated unions
  132. @theburningmonk type PaymentMethod = | Cash | Cheque of ChequeNumber | Card of CardType * CardNumber
  133. Unencapsulated State
  134. @theburningmonk
  135. @theburningmonk public class RecentlyUsedList { private List<string> items = new List<string>(); public List<string> Items { get { return items; } } … }
  136. @theburningmonk immutability
  137. @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
  138. @theburningmonk Affordance an 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.
  139. source https://www.youtube.com/watch?v=aAb7hSCtvGw
  140. @theburningmonk your abstractions should afford right behaviour, whilst make it impossible to do the wrong thing
  141. @theburningmonk “Make illegal states unrepresentable” - Yaron Minsky
  142. @theburningmonk discriminated unions
  143. @theburningmonk type PaymentMethod = | Cash | Cheque of ChequeNumber | Card of CardType * CardNumber finite, closed set of valid states ONLY
  144. closed hierarchy
  145. no Nulls
  146. @theburningmonk match paymentMethod with | Cash -> … | Cheque chequeNum -> … | Card (cardType, cardNum) -> …
  147. @theburningmonk Recap
  148. @theburningmonk immutability
  149. @theburningmonk make illegal state unrepresentable
  150. Getters and Setters
  151. “When it’s not necessary to change, it’s necessary to not change.” - Lucius Cary
  152. “Now we have shortcuts to do the wrong thing. We used to have type lots to do the wrong thing, not anymore.” - Kevlin Henney
  153. @theburningmonk immutability by default
  154. @theburningmonk type Person = { Name : string Age : int }
  155. @theburningmonk type Person = { mutable Name : string mutable Age : int }
  156. @theburningmonk immutability
  157. Uncohesive Tests
  158. @theburningmonk MethodA MethodB When_…Then_… () When_…Then_… () When_…Then_… () When_…Then_… () When_…Then_… () When_…Then_… ()
  159. @theburningmonk MethodA MethodB MethodC FeatureA FeatureB
  160. @theburningmonk complexities & potential bugs in the way methods work together
  161. @theburningmonk …especially when states are concerned
  162. @theburningmonk Test Driven Development
  163. “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
  164. @theburningmonk
  165. @theburningmonk how many tests?
  166. @theburningmonk every test has a cost
  167. @theburningmonk did we cover all the edge cases?
  168. @theburningmonk Property-Based Testing (with FsCheck)
  169. @theburningmonk List.rev reverse + reverse = original length of list is invariant append + reverse = reverse + prepend
  170. @theburningmonk List.rev property : reverse + reverse = original let ``reverse + reverse = original`` rev aList = aList |> rev |> rev = aList Check.Quick (``reverse + reverse = original`` List.rev) // Ok, passed 100 tests.
  171. @theburningmonk List.rev property : 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.
  172. @theburningmonk List.rev property : 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.
  173. @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]" [“HnOq6"; null; false; false; '#'] 6: true [“"] … 11: <null> ['014'; '0'; “nRH”; "<#oe"; true; false; ‘O'] …
  174. @theburningmonk shrinking
  175. @theburningmonk Check.Quick (``append + reverse = reverse + prepend`` id) // Falsifiable, after 2 tests (4 shrinks) (StdGen (1855582125,296080469)): Original: ‘013' ["}k"; ""; “"] Shrunk: true [false]
  176. @theburningmonk let computers do the grunt work
  177. source : http://bit.ly/1kEpEso
  178. @theburningmonk Types vs Tests
  179. @theburningmonk all bugs
  180. @theburningmonk unknown known
  181. @theburningmonk tests types
  182. @theburningmonk tests types
  183. @theburningmonk unit-testing distr. systems system-testing
  184. @theburningmonk Jepsenproperty-based unit-testing system-testing distr. systems
  185. @theburningmonk Jepsenproperty-based unit-testing types as proof TLA+ distr. systems system-testing
  186. Noisy Code Visual Dishonesty Lego Naming Underabstraction Unencapsulated State Getters and Setters Uncohesive Tests
  187. “Practice does not make perfect. Only perfect practice makes perfect.” - Vince Lombardi
  188. “Perfection is not attainable. But if we chase perfection, we can catch excellence.” - Vince Lombardi
  189. “Programming languages have a devious influence: they shape our thinking habits.” - Edsger W. Dijkstra
  190. “One of the most disastrous thing we can learn is the first programming language, even if it's a good programming language.” - Alan Kay
  191. “I’m not a great programmer; I’m just a good programmer with great habits.” - Kent Beck
  192. @theburningmonk what about ineffective coding habits SOME F#/FP programmers DO have?
  193. @theburningmonk
  194. @theburningmonk people are too puritanical about purity
  195. …premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3% - Donald Knuth
  196. @theburningmonk F# Map vs .Net array vs Dictionary
  197. @theburningmonk
  198. @theburningmonk Explicit is better than implicit. - the Zen of Python
  199. @theburningmonk Simple is better than Complex. Complex is better than Complicated. - the Zen of Python
  200. @theburningmonk Special cases aren't special enough to break the rules. - the Zen of Python
  201. @theburningmonk Special cases aren't special enough to break the rules. Although practicality beats purity. - the Zen of Python
  202. @theburningmonk If the implementation is hard to explain, it's a bad idea. - the Zen of Python
  203. @theburningmonk @theburningmonk theburningmonk.com github.com/theburningmonk
  204. @theburningmonk is hiring :-) http://tech.just-eat.com/jobs

×