Quantcast
Channel: dave yarwood
Viewing all articles
Browse latest Browse all 64

_why's (Poignant) Guide to Ruby in Clojure: Part 3

$
0
0

Parts 1 and 2 of this series can be found here and here.

Okay, is it me, or is Chapter 5 absurdly long compared to the other chapters in this book!? Holy moley. I’m going to have to split this one up into a couple of blog entries. This entry will cover roughly half of Chapter 5.

Things start getting super fun in this chapter. I think I may have had even more fun translating these examples than I did reading/exploring them back when I was learning Ruby for the first time. These examples continue to highlight some interesting differences between Ruby and Clojure. For one thing, Ruby has the advantage when it comes to convenient “case statement” syntax. Clojure does have a case function that matches based on the identity of a given object, but Ruby’s case statements are more flexible in that they allow matching based on whether a number falls within a given range. We could mirror that syntax in Clojure fairly easily by using a macro, but it’s easy enough to just use a cond statement instead, like I did below.

I chose to use Clojure records and protocols as I translated the WishMaker and MindReader examples, however a true Clojurian might prefer to use ordinary functions rather than complicate things with records and protocols. For the purposes of this translation exercise, though, I thought it would be interesting to use records and protocols in order to show how they can duplicate some of the functionality of Ruby’s classes. By the way, “endertromb.core” is an imaginary library analogous to _why’s imaginary Endertromb module. As with some of the examples in the last chapter, the “Endertromb” examples here are more an illustration of syntax than anything.

Chapter 5 (Sections 1-3)

; ex. 1:
(defndr-chams-timeline[year](cond(=year1894)"Born."(<=1895year1913)"Childhood in Louisville, Winston Co., Mississippi."(<=1914year1919)"Worked at a pecan nursery; punched a Quaker."(<=1920year1928)(str"Sailed in the Brotherhood of River Wisdomming, which journeyed the Mississippi ""River and engaged in thoughtful self-improvement, where he finished 140 credit ""hours from their Oarniversity.")(=year1929)"Returned to Louisville to pen a novel about time-travelling pheasant hunters."(<=1930year1933)(str"Took up a respectable career insuring pecan nurseries. Financially stable, he ""spent time in Brazil and New Mexico, buying up rare paper-shell pecan trees. ""Just as his notoriety came to a crescendo: gosh, he tried to bury himself ""alive.")(=year1934)(str"Went back to writing his novel. Changed the hunters to insurance tycoons and ""the pheasants to Quakers.")(<=1935year1940)(str"Took Arthur Cone, the Headmaster of the Brotherhood of River Wisdomming, as a ""houseguest. Together for five years, engineering and inventing.")(=year1941)"And this is where things got interesting.")); ex. 2:
(dr-chams-timeline1941); ex. 3:
(cond(=year1894)"Born."(<=1895year1913)"Childhood in Louisville, Winston-Co., Mississippi.":else"No information about this year."); ex. 4:
(if(=1894year)"Born."(if(<=1895year1913)"Childhood in Louisville, Winston Co., Mississippi.""No information about this year.")); ex. 5:
(defopus-magnumtrue)(defnsave-hannah[](let[successopus-magnum]nil)); this doesn't really make sense, but then, the original Ruby example was just to
; demonstrate the lack of closures in Ruby
; ex. 6:
(defverb"rescued")(doseq[verb["sedated""sprinkled""electrocuted"]](println"Dr. Cham"verb"his niece Hannah."))(println"Finally, Dr. Cham"verb"his niece Hannah."); ex. 7:
(doseq[verb["sedated""sprinkled""electrocuted"]](println"Dr. Cham"verb"his niece Hannah."))(println"Finally, Dr. Cham"verb"his niece Hannah."); results in an error, just like the Ruby example
; ex. 8:
(require'[endertromb.core:asendertromb])(defprotocolWishMaking(grant[wmwish]"Grants a wish."))(defrecordWishMaker[energy]WishMaking(grant[_wish](cond(or(>(countwish)10)(re-find#""wish))(throw(IllegalArgumentException."Bad wish."))(zero?energy)(throw(Exception."No energy left.")):else(do(endertromb/makewish)(WishMaker.(decenergy)))))); ex. 9:
(deftodays-wishes(WishMaker.(rand-int6))); ex. 10:
(deftodays-wishes(WishMaker.(rand-int6)))(granttodays-wishes"antlers"); ex. 11:
(defnumber5)(print(incnumber))(defphrase"wishing for antlers")(print(countphrase))(deftoday-wishes(WishMaker.(rand-int6)))(granttodays-wishes"antlers"); ex. 12:
(print(type5))(print(type"wishing for antlers"))(print(type(WishMaker.(rand-int6)))); ex. 13:
(require'[endertromb.core:asendertromb])(defprotocolMindReading(read[mr]"Reads minds."))(defrecordMindReader[minds]MindReading(read[_](mapendertromb/readminds))); Ruby initialize method pulled out into the object instantiation:
(MindReader.(endertromb/scan-for-sentience)); skipping all the irb stuff... (it's not relevant to Clojure)
; ex. 14:
(defprotocalElevation(maintenance-password[this]"Maintenance password.")(authenticate[thispassword]"Checks password before operating."))(defrecordElevator[password]Elevation(maintenance-password[_]"stairs_are_history!")(authenticate[_password](when-not(=passwordmaintenance-password)(throw(Exception."bad password"))))); Other Elevator methods would call (authenticate [_ password]) before executing
; their functions. If the password checks out, it returns nil and the rest of the
; method will happily execute. Otherwise, an exception is thrown.
; ex. 15:
(defnwipe-mutterings-from[sentence](clojure.string/replacesentence#"\([^)]*\)""")); ex. 16:
(defwhat-he-said(str"But, strangely (em-pithy-dah), I learned upon, played upon (pon-shoo) ""the organs on my home (oth-rea) planet."))(wipe-mutterings-fromwhat-he-said); ex. 17:
(defnwipe-mutterings-from[sentence](when-not(string?sentence)(throw(IllegalArgumentException.(str"cannot wipe mutterings from a "(typesentence)))))(clojure.string/replacesentence#"\([^)]*\)""")); ex. 18:
(defsomething-said"A (gith) spaceship.")(wipe-mutterings-fromsomething-said); ex. 19:
; (the "sentence = sentence.dup" example)
; there is no need to do this in Clojure, thanks to functional programming!
; ex. 20:
; (ditto)
; ex. 21:
(defs"A string is a long shelf of letters and spaces.")(subss01); A
(subss0); A string is a long shelf of letters and spaces.
(applystr(drop1(drop-last1s))); (drops first and last character)
(subss03); A s
(re-find#"shelf"s); shelf
; ex. 22:
; (same as ex. 17)
; ex. 23:
(defmuddy-stick"Here's a ( curve.")(wipe-mutterings-frommuddy-stick); not a problem for our functional implementation using clojure.string/replace!
; ex. 24:
; Bingo! Here, _why uses gsub/regexp to improve this method. This is practically the
; same thing as our clojure.string/replace solution; _why just used a slightly
; different regexp, \([-\w]+\) than I did. Mine's better, though ;)
; ex. 25:
; Clojure doesn't encourage monkey-patching, but implementing this is still trivial
; and quite readable using ordinary functions:
(defnname-significance[name](let[legend{"Paij""Personal","Gonk""Business","Blon""Slave","Stro""Master","Wert""Father","Onnn""Mother","ree""AM","plo""PM"}syllables(clojure.string/splitname#"-")](clojure.string/join""(maplegendsyllables)))); BTW, you could technically "monkey-patch" the String type to add a
; "name-significance" method, extended from a protocol you create via Clojure's
; "extend-type" function, but the usage would be exactly the same:
; (name-significance "name"). So in this case, there isn't much point to using
; anything more complicated than an ordinary function.
; ex. 26:
(name-significance"Paij-ree"); ex. 27:
; rather than monkey-patching, you could just do this:
(defndash-split[s](clojure.string/splits#"-")); if polymorphism is needed, you can use multimethods
; ex. 28:
(dash-split"Gonk-plo"); ex. 29:
; (class String; def dash_split; split('-'); end; end)
; The equivalent to this in Clojure would involve using extend-type on Java's String
; class. It's not really necessary, though :)
; ex. 30:
; the equivalent part of our name-significance method is:
(maplegendsyllables); ex. 31:
(defcats-and-tips(map#(+%(*%0.20))[0.120.630.09]))

Hope you enjoyed this as much as I enjoyed translating it! Comment below if I missed anything or if you think you might have a better translation for any of these examples. The rest of Chapter 5 is coming soon – get ready for the Animal Lottery!


Viewing all articles
Browse latest Browse all 64

Trending Articles