Functional Refactoring and "You Can't Get There From Here" 74

Posted by Michael Feathers Wed, 05 Aug 2009 16:18:00 GMT

I’ve been working in Haskell recently, and I find myself doing much less refactoring. In fact, I rewrite more than I refactor.

I’m not talking about massive refactoring, but rather the sort of micro-refactoring that I do to move from one algorithm to another. If you are working in an imperative language, you can often make micro waypoints – little testable steps along the way as you change your code from one shape to another. You can add a variable, confident that behavior won’t change because you haven’t used it yet, and you can move one statement down below another, changing the order of a calculation if (again) you are confident that the overall computation is order in variant. As I TDD my code, I do those sorts of things. I often refactor by introducing a parallel computation in the same method as the old one, and if the test passes, I knock out the old way of doing things and leave the shiny new one.

In Haskell, however, I don’t do that, and it reduces the amount of time that I spend refactoring. Yes, I rename variables, add parameters, and introduce where-clauses, etc. But, refactoring in Haskell seems different, and I think I know why now. There are two reasons and they sort of intermingle.

Imperative programs are built of little state changing variables, and one of the effects is that you can have lots of little bits changing independently. It’s rather easy to add mutating state variables and have “several calculations in the air” in a method while you are along the way toward reducing it to one. In functional programming, however, you use bigger pieces. Instead of looping, you use map or fold – you end up with terse code which packs a lot of punch.

Here’s a function which determines a position within a document from a column/row coordinate (affectionately named x and y here):

absPosition :: Buffer -> Int
absPosition (Buffer (x, y) contents) = x + lenPreviousLines contents
  where lenPreviousLines = foldr ((+).length) 0 . take y . terminatedLines

The thing to get from this example if you don’t read Haskell is that it is fully compositional. Each of the dots in the definition of lenPreviousLines is a composition operation. We apply the terminatedLines function to a string, and take its output and pass it to the function ‘take y’, then we pass its output to ‘foldr ((+).length) 0’. The net effect (take my word for it) is that we end up with the sum of the lengths of a set of text lines.

That small piece of code performs a rather large chunk of work. It takes a string, splits it into lines, appends a newline to each line, takes y of the lines and then sums the count of their characters. But, to me, that’s not the interesting part. The interesting part is that it is tight. There is no space for a parallel computation, no place where you can perform some computation off to the side and progressively move toward a different algorithm. If you want to change the algorithm you essentially rewrite it – you replace some chunk of that code with another chunk of code.

One of the beautiful things about pure functional programming is that it eliminates side effects. That, by itself, might not impress you, but the thing that it enables is very powerful, when you remove side effects you lose all barriers to composability. It’s easy to make little tinker-toy-like parts which you piece together to do your work, and, in fact, that’s exactly what has happened in Haskell. Most of the time, you can find a little function which will do what you want. You don’t often have to drop down into primitive list operations or recursion. Instead, you have this substrate of little composable bits. We don’t have anything like that in traditional languages. We drop down to loops, assignment and mutable variables all the time.

The end result, I think, is that a lot of refactoring in Haskell is more like rewriting, or (to be precise) it is more like the ‘Substitute Algorithm’ refactoring in Martin Fowler’s Refactoring book. In pure code, there isn’t any extra bandwidth for building up parallel computations. The pieces are bigger, so many refactorings are more like leaps than micro-adjustments toward a goal.

Is this good or bad? I don’t know. I think it is just different. Personally, I’m happy to have higher level pieces, although, I have to admit, I sometimes miss the fluidity of work with more granular primitives. It’s a place you can always drop down to and build back up from. In Haskell, at the micro-level, you have to fix your sights on a new target and rebuild.

Comments

Leave a response

  1. Avatar
    Johnicholas about 2 hours later:

    Refactoring in functional programming languages is different, I agree. However, it is possible to do it smoothly, rather than rewriting in big chunks. Paul Hudak has some awesome examples of smooth manipulations in his book “Haskell School of Expression”.

    Don’t throw up your hands and say “it’s different”. Yes, it’s different, but that move is always available – it’s too easy.

    Two of the primary moves in functional refactoring are expansion “replace a function with its (anonymous) definition”, and contraction “replace an (anonymous) function with a call to an existing procedure”. Elegant chains often look like “expand-expand-expand-contract-contract-contract”.

  2. Avatar
    Michael Feathers about 3 hours later:

    Johnicholas: Yes, I do that. The bit which I don’t do is build up a parallel computation in place, and use it to replace what was there. It also seems that when I add new functionality, I have to replace what I have for the next case rather than perform a minor refactoring. It’s manageable, but a bit different.

    BTW, I like Hudak’s book quite a bit. I was my first Haskell book.

  3. Avatar
    Ben Sanders about 3 hours later:

    Can you talk a bit more about TDD in Haskell? I was having good luck on the bottom ‘layer’ of what I was working on, but trying to test-drive higher stuff (that composed those others) was quite hard for me – had to do a lot of setup. Maybe I am just going about things wrong – I still don’t have a good idea how to structure Haskell programs overall.

  4. Avatar
    Julian Morrison about 4 hours later:

    I think type-classes are underused in idiomatic Haskell. Working with those gives you both the ability to test with mocks, and often the ability to refactor. Particularly, classical FP tends to need a million little changes when you change the data definition, but accessing data through type-classes gives you protection.

  5. Avatar
    Tony Morris about 5 hours later:

    I know you’re learning but a couple of statements are completely out of whack.

    “Imperative programs are built of little state changing variables, and one of the effects is that you can have lots of little bits changing independently.”

    Actually, imperative programs, by their very definition, have little bits that are not little bits after all, since they are not independent. This is the essence of the whole problem of imperative programming.

    “when you remove side effects you lose all barriers to composability.”

    No, quite the opposite, when you remove side-effects, you break down barriers to composability.

    I think you’re one step before realising that “refactoring” is a bunk concept that (poorly) works around bad programming practices.

    When you want to “change the behaviour” of that pure function, why the aversion to writing a new function? Why does the original one need to be “refactored”? The answer runs deep, so spare some time for yourself.

  6. Avatar
    Arnaud about 5 hours later:

    @Julian There is an enlightening example of this in RWH that demonstrates use of type-classes in a testing context for monadic code (but could be any typeclass of course). Nice thing about it is that it is deeply related to something that has become prominent in OO testing: test and mock to interfaces you own, thus creates your own abstraction that wrap standard or low-level interfaces.

    But you may know it already :-)

    @michael did you try to install/use HaRe ? it seems a little bit dormant (or so was it last time I checked…) but promised to offer some useful tools for doing simple refactorings. The ones I am missing most is the ability to rename and ‘extract function’.

  7. Avatar
    Carl about 6 hours later:

    ““when you remove side effects you lose all barriers to composability.”

    No, quite the opposite, when you remove side-effects, you break down barriers to composability.”

    I think you don’t read so good. And you’re mean.

  8. Avatar
    TM about 10 hours later:

    @Carl:

    That’s Tony Morris for you. Do like we all do on the various FP mailing-lists: read his messages just to discover what other twisted wording he’ll come up with to make fun of the person he responds to but ignore the content of his messages, which is often wrong anyway (this one is no exception).

  9. Avatar
    Paul Prescod about 12 hours later:

    I think Tony Morris is saying something interesting: if you just write a second function then you have two parallel paths (the new one and the old one) which are much easier to test against each other than if you had two (heap-changing) algorithms in a single method. You could, for example, auto-generate test data and throw it at both versions pretty easily. When there are discrepancies you can look to see whether the new one or the old one handled it better.

  10. Avatar
    chak@cse.unsw.edu.au about 12 hours later:

    Concerning both refactoring and rewriting a function, we Haskellers also rely a lot on the type checker to keep us on the right path. Of course, strong type checking cannot replace a rigorous testing discipline, but it makes you more confident to take bigger steps.

  11. Avatar
    Manuel Chakravarty about 12 hours later:

    Oops .. for entering my email instead of the name.

  12. Avatar
    Brandon about 18 hours later:

    You can always introduce a let and name those intermediate results, and then there’s plenty of room for parallel versions, if you really want to change things incrementally.

  13. Avatar
    Paul Bartlett about 19 hours later:

    Not sure I’m adding much here, but when I was working with CAL (http://openquark.org/ – a Haskell-like language which compiles to Java bytecode) I found the same.

    I’d solve the problem one way, learning much about it, then almost completely rewrite it. I guess this is much more akin to prototyping than refactoring, which isn’t itself a bad thing IMHO. Wasn’t it Brooks that said: “Plan to throw one away; you will anyway”?

  14. Avatar
    Don Wells about 22 hours later:

    I like to work in Lisp (functional programming) when ever I can. I originally learned it the 70s before we bothered with things like TDD. But I do things differently now. What I have learned to do now is create a stack of failing unit tests.

    You drive your code from the top level by adding a test that fails. You go to fix your code and notice you need a new function. You stop and create another unit test for that function. Now as you create that function you notice you need yet another function. So you create a failing unit test for that.

    Now you have 3 failing unit tests in a stack. When I get the current unit test done I will push another on the stack until my function is done, then pop the stack. Now I have the function I need to get the next test running. I can now test my function into existence and pop the stack again. I use the function I just created to get the last unit test running.

    So, now to refactor. I decide to change some function by calling some new function, so I write unit tests to test that new function into existence then continue my refactoring. If I find a function not being used I delete it and all of its tests.

  15. Avatar
    Tristan Seligmann 1 day later:

    “If you want to change the algorithm you essentially rewrite it – you replace some chunk of that code with another chunk of code.”—that’s pretty much how I’d describe the activity of refactoring; you seem to be taking a more limited definition than I would use.

  16. Avatar
    sterl. 4 days later:

    Using let or where bindings to pull out pieces of a computation and experiment with alternatives is very good advice. But also, I find myself coding large complex actions on lists on the first pass as big recursive loops, then pulling out bits of common structure into smaller pieces. I suppose the difference is that you can always step back and look at the whole chain of computations, so refactoring will tend to involve altering where/how the chain is broken up to make things more logical. Eliminating superfluous points and soforth as well.

    foldr ((+).length) 0 . take y . terminatedLines for example could become (sum . map length . take y . terminatedLines)—moving from explicit folds up to more restricted combinators is an abstraction win i think, though your milage my vary.

    At which point I’d probably decide the where clause was superfluous and inline it back into the function yielding: x + (sum . map length . take y . terminatedLines $ contents). I’d then turn the plus into a section, getting: (x+) . sum . map length . take y . terminatedLines $ contents.

    At this point I’d see that adding x when I’m already summing is sort of silly, and maybe rewrite it as such: sum . (x:) . map length . take y . terminatedLines $ contents.

    This now expresses that the sum is of the length of y terminated lines, along with the x position.

    Your taste and mileage may vary of course.

  17. Avatar
    Michael Feathers 6 days later:

    sterl: Thanks for your suggestions. I incorporated them and added some musings here: http://blog.objectmentor.com/articles/2009/08/11/naming-and-body-language-in-functional-code

  18. Avatar
    cheap vps about 1 year later:

    cheap VPS

  19. Avatar
    Hermes belts about 1 year later:

    Hermes belt, Hermes belts, Hermes Mens belts, Hermes belts for men.
    To get the beachy, wavy look it helps if your hair has some natural wave to it and this look works on hair of almost any length, even chin-length hair or a bit shorter.

  20. Avatar
    gucci wallet about 1 year later:

    gucci wallet, gucci wallets, mens gucci wallet, women gucci wallet.
    To get the beachy, wavy look it helps if your hair has some natural wave to it and this look works on hair of almost any length, even chin-length hair or a bit shorter.

  21. Avatar
    louis vuitton wallet about 1 year later:

    louis vuitton wallet, louis vuitton wallets, mens louis vuitton wallet, women louis vuitton wallet.
    Bedrooms are a place for rest and relaxation. The simplicity of modern bedroom designs creates a calmness that makes that feeling even more prevalent, There are funny and strange bedrooms with different shapes .

  22. Avatar
    dupont lighter about 1 year later:

    dupont lighter, dupont lighters, st dupont lighter, s.t. dupont lighters. As classical music evolved, distinctive characteristics developed. Changes in form were seen along with changes in phrase structure.

  23. Avatar
    replica louis vuitton belt about 1 year later:

    replica louis vuitton belts, replica lv belts, replica lv belts, Elegant replica louis vuitton belt, Fashion replica louis vuitton belts for men, replica louis vuitton mens belt.
    Adjusting your diet to a healthier way of eating is a way to lose belly fat without dieting that works for many people. Instead of putting yourself on a restrictive and often unsafe diet, you can try adjusting the way you eat. Instead of five cookies, maybe have one small slice of low fat angel food cake. Simple exchanges like that will provide you with a way of eating that will keep you healthy your entire life.

  24. Avatar
    armani belt about 1 year later:

    armani belt, armani belts, armani belts for men, armani mens belt. The biggest benefit with classic styles is that they’re never trendy. Trendy styles are meant to push the boundaries and draw attention to the garment. A trendy look is meant to make a bold statement and never slips gracefully into the fashion parade.

  25. Avatar
    Pandora about 1 year later:

    The replies were numerous and vociferous. Dave Astels poignantly stated that hand-rolling mocks is so 2001!

  26. Avatar
    http://www.blacktowhiteiphone4.com about 1 year later:

    white iphone 4 available now. It is the best news today.

  27. Avatar
    Silicone Molding about 1 year later:

    Mold making is the core business of Intertech (Taiwan). With world level technology, Intertech enjoys a very good reputation for making Injection Mold and Plastic Moldsfor their worldwide customers.

  28. Avatar
    Hermes belts about 1 year later:

    hermes belt, Gucci Man,beltman.

  29. Avatar
    replica tag heuer carrera about 1 year later:

    The replies were numerous and vociferous. Dave Astels poignantly stated that hand-rolling mocks is so 2001!

  30. Avatar
    accounting services about 1 year later:

    I think you’re one step before realising that “refactoring” is a bunk concept that (poorly) works around bad programming practices.accounting services

  31. Avatar
    iPhone Contacts Backup For Mac about 1 year later:

    Thanks for shareing! I agree with you. The artical improve me so much! I will come here frequently. iPhone Contacts Backup For Mac can help you backup iphone contact to mac.

  32. Avatar
    interactive intelligence about 1 year later:

    Can you do a more detailed piece on “micro waypoints,” I think I am getting the hang of testing, but I am still a beginner.

  33. Avatar
    Criminal Records about 1 year later:

    I think that programmers communicate with structure as much as they communicate with names. It’s the body-language of their code.

  34. Avatar
    rolexes watches about 1 year later:

    As a matter of truth, you will find some factors for individuals to decide the value of the rolex watches. In order to get precious rolexes watches for collecting, you’ll want to pay attention to the following aspects. At 1st, you need to judge the amount of the fake rolex. The amount of the Rolex DateJust watches existing in rolex watches the world is crucial. If Rolex Daytona is exclusive, and it has a small amount within the world, Swiss Replica Watch is going to become far more useful replica watches with the development of years. A widespread Discount Watches just isn’t worth to collect. Useful Rolex Day Date are available in tiny amounts. The distinct looks of these designer fake watches are extremely unique. In most models, the face dials generally feature a layered design. This makes the brand genuinely intriguing and attractive. The cheap watches are created from a few of the finest supplies utilized in contemporary Breitling Watches generating. Copy Watches supplies incorporate solid Fake Watches state and refined stainless steel, diamonds, reinforced crystals and other precious stones. In some models, the bracelets consist of a mixture of gold and stainless steel while a much more casual leather band is also available. The Omega Watches of Raymond Weil are mysterious and intriguing on account of the special textured layering of the front faces. Do not buy sport Cartier Watches if you are an office lady. The replica watches are an individual belonging that represents the taste and the style of a person. Imitation Watches will be easy for you if you have already known about yourself well. At last, we bet you can find the ideal women’s replica watches uk for the women in your life as long as you do research correctly.

  35. Avatar
    Colin about 1 year later:

    You can add a variable, confident that behavior won’t change because you haven’t used it yet

  36. Avatar
    Sunglass about 1 year later:

    Buy $10 Replica Designer Sunglasses with 3-day FREE SHIPPING this is it

  37. Avatar
    Yalova Emlak about 1 year later:

    Thank you very much for this article and blog.I like its and as to me it’s good job.

  38. Avatar
    http://www.beltsok.com/hermes-belt.html about 1 year later:

    Hermes belt store offers high quality and cheap designer belts, please have a look.

  39. Avatar
    http://beltsok.com about 1 year later:

    There are various kinds of Hermes belts available to match your needs.

  40. Avatar
    http://www.smartbargainonline.com about 1 year later:

    Any lady with wholesale beads wedding on the brain will be dropping not so faint hints beside the wholesale jewelry beads way so pay consideration when you’re receiving prepared to pop wholesale pandora beads the query. You can also seem at the jewelry she already owns to get an clue of her chic, especially regarding what type of wholesale fashion jewelry metal she loves. Keep in mind that she will be got up in this ring for the rest of her life so make definite to choose something that suits her personality.

  41. Avatar
    dory about 1 year later:

    This is a good post. This post give truly quality information.I’m definitely going to look into it. Really very useful tips are provided here.thank you so much.Keep up the good works. Social Network

  42. Avatar
    tiffany and co outlet about 1 year later:

    A bad beginning makes a bad ending. A bird in the hand is worth than two in the bush. A year’s plan starts with

    spring. Bad news has wings. Better to ask the way than go astray. By reading we enrich the mind, by conversation we

    polish it. Custom makes all things easy. He is not fit to command others that cannot command himself. He laughs best

    who laughs last. tiffany and co outlet

  43. Avatar
    Big pony about 1 year later:

    Thank you for sharing,it is very helpful and I really like it!

  44. Avatar
    rolex replica watch about 1 year later:

    i remember when math was much simpler in early school years where calcualtors had just become very affordable. This seems too complicated.

  45. Avatar
    okey oyunu oyna about 1 year later:

    great article.

    internette görüntülü olarak okey oyunu oyna, gerçek kisilerle tanis, turnuva heyecanini yasa.

  46. Avatar
    ford leveling kit about 1 year later:

    I love sharing information Thank you :-)

  47. Avatar
    leveling kit ford about 1 year later:

    Your post is informative I love it Thanks a lot :-)

  48. Avatar
    leveling kit f250 about 1 year later:

    I love your post. Thank you :-)

  49. Avatar
    f350 leveling kit about 1 year later:

    I will always check your website if there is always a new article. Thank you :-)

  50. Avatar
    mbt shoes sales about 1 year later:

    thank you for posting this SO MUCH.

  51. Avatar
    Jewellery about 1 year later:

    teri dhadkano ko chulu

  52. Avatar
    beats by dr dre headphones about 1 year later:

    Beats by dr dre studio with look after talk in white. extra attributes on Monster Beats By Dr. Dre Pro Headphones Black a specific tri-fold design and design and carrying circumstance which make for compact and uncomplicated safe-keeping when not in use. Beats by dr dre solo .

  53. Avatar
    [URL=http://www.suprafootwearonsale.com]supra footwear store[/URL] about 1 year later:

    thanks for the information..It’s good to see this information in your post, i was looking the same but there was not any proper resource, thanx now i have the link which i was looking for my research.

  54. Avatar
    <a href="http://www.suprafootwearonsale.com">supra footwear store</a> about 1 year later:

    supra footwear store

  55. Avatar
    cheap supras about 1 year later:

    Oh! Good I have read your topic I’m really super success Written cheap supras shoes…! I believe you’ve made certain pleasant points in the topic likewise supras shoes. I guess you should not stop with this supras for sale; I would like to see more of your submissions vibram 5 fingers.

  56. Avatar
    deadbeat millionaire review about 1 year later:

    Would you like to banckup iphone SMS to mac, macBook, macbookPro as .txt files? Now a software iphone SMS to Mac Backup can help you to realize it.

  57. Avatar
    dreamy gift house about 1 year later:

    Wow. You can listen to the track right here, and happy divine thoughts about the universe everybody!

  58. Avatar
    beats by dr dre over 2 years later:

    as looking the same but there was not any proper resource, thanx now i have the link which i was looking for my research.beats by dr dre beats by dre sale

  59. Avatar
    bagsupplyer over 2 years later:

    Thank you for posting. Waiting for updating.New fashion women LV purses AAA with best quality for wholesale on line from China free shipping

  60. Avatar
    Ashley Bowling over 2 years later:

    Cleanliness is next to godliness Clothes make the man Cold hands, warm heart

  61. Avatar
    ysbearing/yisong@1stbearing.com over 2 years later:

    Slewing bearing called slewing ring bearings, is a comprehensive load to bear a large bearing, can bear large axial, radial load and overturning moment. http://www.1stbearing.com

  62. Avatar
    christian louboutin over 2 years later:

    The professional design make you foot more comfortable. Even more tantalizing,this pattern make your legs look as long as you can,it will make you looked more attractive.Moveover,it has reasonable price.If you are a popular woman,do not miss it.

    Technical details of Christian Louboutin Velours Scrunch Suede Boots Coffee:

    Color: Coffee
    Material: Suede
    4(100mm) heel
    Signature red sole x

    Fashion, delicate, luxurious Christian louboutins shoes on sale, one of its series is Christian Louboutin Tall Boots, is urbanism collocation. This Christian louboutins shoes design makes people new and refreshing. Red soles shoes is personality, your charm will be wonderful performance.

  63. Avatar
    beats by dr dre over 2 years later:

    A university studentbeats by dr dre caught by the enemy, the enemy tied him at the poles,just beats solo headphones purple and then asked him: say, where are you? You do not say it electrocuted! Scheap dr.dre beats studio headphones balck/yellowtudents back to the enemy a word, the result was electrocuted, he said: I am TVU.Hot sale beats by dr dre pro headphones

  64. Avatar
    Spyder Jackets over 2 years later:

    http://www.spyderjackets-outlet.net

  65. Avatar
    Arcteryx Jackets over 2 years later:

    http://www.arcteryxjackets-sale.com

  66. Avatar
    tv show over 2 years later:

    thanks a lot for sharing. /danielle

  67. Avatar
    Backup iphone sms over 2 years later:

    Stop data lose on the mobile. So you can export the files to HDD.

  68. Avatar
    youngbrown over 3 years later:

    Thanks for the information, I’ll visit the site again to get update information video games

  69. Avatar
    anoymous over 3 years later:

    Now that I’ve Provided the 1st of my Vegas talks I DesiRed-colored to Article Almost everything On line for Anybody who couldn’t Go to in Particular person.ralph lauren shirts Slides are Right here (OpenOffice Structure)WStrikeepaper is Right here (Pdf file)No.-ME Resource code is…At present kinda ugly.ralph lauren shirtsralph lauren hats If Anybody In fact Would like a Duplicate Allow me know and I’ll Thoroughly clean it up for Launch.217 Ft is the Variety I Arranged; I Think that’s a Globe Report (beating Each the 69 Ft from Flexilis at Defcon 13 and the 65 meters claimed by ThingMagic in a Yahoo Tech Speak). My Gear is capable of Much Extra but I Strike the Restrict of my Variety;

    Womens Pony Polo

    a chainlink fence a Number of hundRed-colored yards Aside was reflecting the RF Energy, meaning that Extra Energy led to Higher interferenceUsa Style Custom Ralph Lauren will be the Standard outfitter of the US Olympic and Paralympic Clubs for London 2012, Creating the opening and closing ceremony parade uniforms.Ralph Lauren previously Developed the Groups’ uniforms for the 2008 Game titles in Beijing and 2010 Winter Game titles in Vancouver.

    Ralph Lauren Tees

    In Add-on, Ralph Lauren will Design and style a Selection of village Put on Clothing and Add-ons inspiRed-colored by the 1948 Olympic Game titles in London. The vintage motifs will Combine with Modern day styling in Usa Colours of Red-colored, wStrikee and navy and new Olympic Group logo.The Ralph Lauren 2012 Olympic Collection Characteristics reissued heritage pieces from the 1930s and ‘40s, Which includes fleece Cozy-ups, a cricket-Training collar shirt and sweatshirts with United states appliqués.

    Ralph Lauren Sweaters

    Reproductions of Monitor & Area Design and styles Characteristic an updated 1948 crest.

  70. Avatar
    dr dre studio sale over 3 years later:

    If you are in search of nice Dr Dre Beats,our online store will be your first choice. Welcome. The Dr Dre Beats Studio is purchased by a lot of people due to its superior quality and low price. Start your shopping now! The Dr Dre Beats Tour Black is taking a large market now, so buy one at once. We accept paypal. Everybody like this world famous brand Dr Dre Beats Pro because of its finest quality but cheap price. You can order one too. Buy it quickly. More surprise are waiting for you if you order our AAA quality and cheaper price Dr Dre Beats Solo HD. Never lose this opportunity. We can supply you this most people loved Dr Dre Beats In Ear with fast delivery and free shipping. Why don’t you seize this precise chance to change your style by buying our latest Beats Studio Sale. Move your finger to log in. How can you stand still when seeing so attractive Dr Dre Beats Limited? Won’t your heart beat more fast after seeing our eyecatching Dr Dre Beats Sale Online? What are you considering? Rush to purchase!!!!We are waiting for your coming!! Beats Dr Dre are worthy owning. Well-known Dr Dre Beats Studio Black Yellow have the heights range from around the ankle to above the knee, and they are available in different colours. This Beats By Dr Dre .

  71. Avatar
    bladeless fans over 3 years later:

    Functional Refactoring and “You Can’t Get There From Here” 70 good post37

  72. Avatar
    louboutin sales over 3 years later:

    Functional Refactoring and “You Can’t Get There From Here” 71 hoo,good article!!I like the post!157

  73. Avatar
    Injection mold over 3 years later:

    Intertech Machinery Inc. provides the most precise Plastic Injection Mold and Rubber Molds from Taiwan. With applying excellent unscrewing device in molds,

    Intertech is also very professional for making flip top Cap Molds in the world. Mold making is the core business of Intertech (Taiwan). With world level technology, Intertech enjoys a very good reputation for making Injection Mold and Plastic Molds for their worldwide customers.

  74. Avatar
    sac birkin over 3 years later:

    When it comes to fashion, each and everyone wants to look the best?

Comments