Sei sulla pagina 1di 12

Adventures in Functional Programming With Ruby

Jul 17th, 2012 The following is an aimless journey through a degenerate form of Ruby, in an effort to learn a bit more about functional programming, simplicity, and A ! design" #uppose that the only way we ha$e to organi%e code in Ruby is to ma&e lambdas, and the only way we ha$e to structure data are arrays'
1 2 3 4 5 6 7 8 square = ->(x) { x * x } square.(4) # => 16 person = [" a!e""#$a%e& pr'n()person = ->((na$e"*en+er)) { pu(s "#{na$e} 's a #{*en+er}" } pr'n()person.(person)

This is the most bare(bones essence of functional programming' all we ha$e is functions" )et*s write some real(ish code this way and see how far we get before it starts becoming painful" #uppose we want to manipulate a database of people, and someone has pro$ided us a few functions to interact with a data store" +e want to use these to add a ,! and some $alidations" -ere*s how we interact with our data store'
1 2 3 4 'nser()person.(na$e",'r(-+a(e"*en+er) # => re(urns an '+ up+a(e)person.(ne.)na$e"ne.),'r(-+a(e"ne.)*en+er"'+) +e%e(e)person.('+) /e(0-)person.('+) # => re(urns (-e na$e" ,'r(-+a(e" an+ *en+er as an arra1

.irst, we need to be able to add a person to our database, along with some $alidations" +e*ll get this data from user input /we can assume that pu(s and *e(s are built(ins that wor& as e0pected1'
1 2 3 4 5 6 7 8 pu(s "2a$e3" na$e = *e(s pu(s "4'r(-+a(e3" ,'r(-+a(e = *e(s pu(s "5en+er3" *en+er = *e(s

+e need a function to do our $alidations and add a person to the database" +hat might it loo& li&e2 !t should accept the attributes of a person and return either an id /on successfully $alidation and insertion1, or an error message, representing what went wrong" #ince we don*t ha$e e0ceptions or hashes ( just arrays ( we*re going to ha$e to get creati$e"

)et*s create a con$ention in our system that e$ery business logic methods returns an array of si%e 2" The first element is the return $alue on success, and the second element is an error message on failure" The presence or absence of data in one of these slots indicates the result" 3ow that we*$e sorted out what we accept as arguments and what we*re going to return, let*s write our function'
a++)person = ->(na$e",'r(-+a(e"*en+er) { 1 re(urn [n'%""2a$e 's requ're+"& 2 re(urn [n'%""4'r(-+a(e 's requ're+"& 3 88 4 re(urn [n'%""5en+er 's requ're+"& 5 re(urn [n'%""5en+er $us( ,e 8$a%e8 or 8/e$a%e8"& 6 *en+er 9= 8/e$a%e8 7 8 '+ = 'nser()person.(na$e",'r(-+a(e"*en+er) 6 [[na$e",'r(-+a(e"*en+er"'+&"n'%& } '/ 7(r'n*(na$e) == 88 '/ 7(r'n*(,'r(-+a(e) == '/ 7(r'n*(*en+er) == 88 '/ *en+er 9= 8$a%e8 ::

!f you aren*t familiar with 7(r'n*(), it is a function that coalesces nil to the empty string, so we don*t ha$e to chec& for both" +ith this function, what we*d li&e to do is call it in a loop until the user has pro$ided correct input, li&e so'
1 'n!a%'+ = (rue 2 .-'%e 'n!a%'+ 3 pu(s "2a$e3" 4 na$e = *e(s 5 pu(s "4'r(-+a(e3" 6 ,'r(-+a(e = *e(s 7 pu(s "5en+er3" 8 *en+er = *e(s 6 resu%( = a++)person.(na$e",'r(-+a(e"*en+er) 1; '/ resu%([1& == n'% 11 pu(s "7u00ess/u%%1 a++e+ person #{resu%([;&[;&}" 12 'n!a%'+ = /a%se 13 e%se 14 pu(s "<ro,%e$# #{resu%([1&}" 15 en+ 16 en+

4f course, we ne$er said anything about .-'%e loops '1 #uppose we don*t ha$e them"

Loops are just functions (called recursively)


To loop, we simply wrap our code in a function and call it recursi$ely until we achie$e the desired result"
1 *e()ne.)person = -> { 2 pu(s "2a$e3" 3 na$e = *e(s 4 pu(s "4'r(-+a(e3" 5 ,'r(-+a(e = *e(s 6 pu(s "5en+er3"

7 *en+er = *e(s 8 resu%( = a++)person.(na$e",'r(-+a(e"*en+er) 6 '/ resu%([1& == n'% 1; pu(s "7u00ess/u%%1 a++e+ person #{resu%([;&[;&}" 11 resu%([;& 12 e%se 13 pu(s "<ro,%e$# #{resu%([1&}" 14 *e()ne.)person.() 15 en+ 16 } 17 18 person = *e()ne.)person.()

+e can en$ision that our code is going to ha$e a lot of '/ resu%([1& == n'% in it, so let*s wrap it in a function" The great thing about functions is that they allow us to re(use structure, as opposed to logic" The structure here is chec&ing for an error and doing one thing on success and another on error"
1 -an+%e)resu%( = ->(resu%("on)su00ess"on)error) { 2 '/ resu%([1& == n'% 3 on)su00ess.(resu%([;&) 4 e%se 5 on)error.(resu%([1&) 6 en+ 7}

3ow, our *e()ne.)person function abstracts away the error handling'


1 *e()ne.)person = -> { 2 pu(s "2a$e3" 3 na$e = *e(s.0-o$p 4 pu(s "4'r(-+a(e3" 5 ,'r(-+a(e = *e(s.0-o$p 6 pu(s "5en+er3" 7 *en+er = *e(s.0-o$p 8 6 resu%( = a++)person.(na$e",'r(-+a(e"*en+er) 1; 11 -an+%e)resu%(.(resu%(" 12 ->(('+"na$e",'r(-+a(e"*en+er)) { 13 pu(s "7u00ess/u%%1 a++e+ person #{'+}" 14 ['+"na$e",'r(-+a(e"*en+er"'+& 15 }" 16 ->(error)$essa*e) { 17 pu(s "<ro,%e$# #{error)$essa*e}" 18 *e()ne.)person.() 16 } 2; ) 21 } 22 23 person = *e()ne.)person.()

3otice what the use of -an+%e)resu%( allows us to e0plicitly name $ariables, instead of using Array de(referencing" 3ot only can we name error)$essa*e, but, using Ruby*s array( e0traction synta0, we can 5e0plode6 our person array into its attributes $ia the (('+"na$e",'r(-+a(e"*en+er)) synta0"

#o far, so good" This code is probably a bit weird loo&ing, but it*s not terribly $erbose, or comple0"

Clean code uses more functions


4ne thing that might seem odd is that our person has no real structure or formal definition" +e simply ha$e an array, and a con$ention that the first element is the name, second element is birthdate, etc" 4ur domain is pretty simple as(is, but let*s suppose we want to add a new field' title" +hat happens to our code when we do this2 4ur database team deli$ers new $ersions of 'nser()person and up+a(e)person to us'
1 'nser()person.(na$e",'r(-+a(e"*en+er"('(%e) 2 up+a(e)person.(na$e",'r(-+a(e"*en+er"('(%e"'+)

+e then ha$e to update our a++)person method'


a++)person = ->(na$e",'r(-+a(e"*en+er"('(%e) { re(urn [n'%""2a$e 's requ're+"& re(urn [n'%""4'r(-+a(e 's requ're+"& == 88 re(urn [n'%""5en+er 's requ're+"& 88 re(urn [n'%""5en+er $us( ,e 8$a%e8 or 8/e$a%e8"& *en+er 9= 8/e$a%e8 '+ = 'nser()person.(na$e",'r(-+a(e"*en+er"('(%e) } [[na$e",'r(-+a(e"*en+er"('(%e"'+&"n'%& '/ 7(r'n*(na$e) == 88 '/ 7(r'n*(,'r(-+a(e) '/ 7(r'n*(*en+er) == '/ *en+er 9= 8$a%e8 ::

1 2 3 4 5 6 7 8 6 1;

And, since we use these e0tractions in *e()ne.)person, that has to change, too" ,gh'
1 *e()ne.)person = -> { 2 pu(s "2a$e3" 3 na$e = *e(s.0-o$p 4 pu(s "4'r(-+a(e3" 5 ,'r(-+a(e = *e(s.0-o$p 6 pu(s "5en+er3" 7 *en+er = *e(s.0-o$p 8 pu(s "='(%e3" 6 ('(%e = *e(s.0-o$p 1; 11 resu%( = a++)person.(na$e",'r(-+a(e"*en+er"('(%e) 12 13 -an+%e)resu%(.(resu%(" 14 ->((na$e",'r(-+a(e"*en+er"('(%e"'+)) { 15 pu(s "7u00ess/u%%1 a++e+ person #{'+}" 16 ['+"na$e",'r(-+a(e"*en+er"('(%e"'+& 17 }" 18 ->(error)$essa*e) { 16 pu(s "<ro,%e$# #{error)$essa*e}" 2; *e()ne.)person.() 21 } 22 ) 23 }

This is the $ery definition of high(coupling" *e()ne.)person really shouldn*t care about the particular fields of a person9 it should simply read them in, and then pass them to a++)person" )et*s see if we can ma&e that happen by e0tracting some of this code into new functions"
1 2 3 4 5 6 7 8 6 1; 11 12 13 14 15 16 17 18 16 2; 21 22 23 24 25 26 rea+)person)/ro$)user = -> { pu(s "2a$e3" na$e = *e(s.0-o$p pu(s "4'r(-+a(e3" ,'r(-+a(e = *e(s.0-o$p pu(s "5en+er3" *en+er = *e(s.0-o$p pu(s "='(%e3" ('(%e = *e(s.0-o$p [na$e",'r(-+a(e"*en+er"('(%e& } person)'+ = ->(*)"'+) { '+ } *e()ne.)person = -> { -an+%e)resu%(.(a++)person.(*rea+)person)/ro$)user.()) ->(person) { pu(s "7u00ess/u%%1 a++e+ person #{person)'+.(person)}" person }" ->(error)$essa*e) { pu(s "<ro,%e$# #{error)$essa*e}" *e()ne.)person.() } ) }

+e*$e now abstracted the way in which we store a person into two functions' rea+)person)/ro$)user and person)'+" At this point, *e()ne.)person will not need to change if we add more fields to a person" !f you*re confused about the use of * in this code, here*s a brief e0planation' * allows us to treat an array as a list of arguments and $ice $ersa" !n person)'+, we use the parameter list *)"'+, which tells Ruby to place all arguments to the function, sa$e the last, into the $ariable ) /so(named because we don*t care about its $alue1, and place the last argument in the $ariable '+" This only wor&s in Ruby 1":9 in 1"; only the last argument of a function may use the * synta0" .urther, when we call a++)person, we use the * on the results of rea+)person)/ro$)user" #ince rea+)person)/ro$)user returns an array, we want to treat that array as if it were an argument list, since a++)person accepts e0plicit arguments" The * does that for us" 3ice< =ac& to our code, you*ll note that we still ha$e coupling between rea+)person)/ro$)user and person)'+" They both are intimate with how we store a person in an array" .urther, if we added new features to actually do something with our people database, we can en$ision more methods coupled to this array(based format" +e need some sort of data structure"

>

!ata structures are just functions


!n non(degenerate Ruby, we*d probably ma&e a class at this point, or at least us a >as-, but we don*t ha$e access to those here" ?an we ma&e a real data structure just using functions2 !t turns out we can, if we create a function that treats its first argument as an attribute of our data structure'
1 2 3 4 5 6 7 8 6 1; 11 12 13 14 ne.)person = ->(na$e",'r(-+a(e"*en+er"('(%e"'+=n'%) { re(urn ->(a((r',u(e) { re(urn '+ '/ a((r',u(e == #'+ re(urn na$e '/ a((r',u(e == #na$e re(urn ,'r(-+a(e '/ a((r',u(e == #,'r(-+a(e re(urn *en+er '/ a((r',u(e == #*en+er re(urn ('(%e '/ a((r',u(e == #('(%e n'% } } +a!e = ne.)person.(" a!e""";6-;1-1674"""$a%e"""4aron") pu(s +a!e.(#na$e) # => " a!e" pu(s +a!e.(#*en+er) # => "$a%e"

acts li&e a constructor, but instead of returning an object /which don*t e0ist for us1, we return a function that, when called, can tell us the $alues of the $arious attributes of our person" +e e0plicitly itemi%e the possible attributes, so we ha$e a fairly firm definition of what the type of a person is"
ne.)person

?ompare this to a class that does the same thing'


1 2 3 4 5 6 7 8 6 1; 11 12 13 14 0%ass <erson a((r)rea+er #'+" #na$e" #,'r(-+a(e" #*en+er" #('(%e +e/ 'n'('a%'?e(na$e",'r(-+a(e"*en+er"('(%e"'+=n'%) @'+ = '+ @na$e = na$e @,'r(-+a(e = ,'r(-+a(e @*en+er = *en+er @('(%e = ('(%e en+ en+ +a!e = <erson.ne.(" a!e""";6-;1-1674"""$a%e"""4aron") pu(s +a!e.na$e pu(s +a!e.*en+er

!nteresting" The si%e of these two bits of code is more or less the same, but the class(based $ersion is full of special forms" #pecial .orms are essentially magic pro$ided by the language or runtime" To understand this code, you need to &now'

what 0%ass means that calling ne. on the class*s name calls the 'n'('a%'?e methods what methods are that prepending @ to a $ariable ma&es it pri$ate to the class* instance the difference between a class and an instance what a((r)rea+er does @

?ompared to our functional $ersion, all you need to &now is'


how to define a function how to in$o&e a function

)i&e ! said, ! find this interesting" +e ha$e two ways of writing essentially the same code, and one way reAuires you to ha$e a lot more special &nowledge than the other" 4B, now that we ha$e a real data structure, let*s rewor& our code to use it, instead of arrays'
rea+)person)/ro$)user = -> { pu(s "2a$e3" na$e = *e(s.0-o$p pu(s "4'r(-+a(e3" ,'r(-+a(e = *e(s.0-o$p pu(s "5en+er3" *en+er = *e(s.0-o$p pu(s "='(%e3" 1 ('(%e = *e(s.0-o$p 2 3 ne.)person.(na$e",'r(-+a(e"*en+er"('(%e) 4 } 5 6 a++)person = ->(person) { 7 re(urn [n'%""2a$e 's requ're+"& '/ 7(r'n*(person. 8 (#na$e)) == 88 6 re(urn [n'%""4'r(-+a(e 's requ're+"& '/ 7(r'n*(person. 1; (#,'r(-+a(e)) == 88 11 re(urn [n'%""5en+er 's requ're+"& '/ 7(r'n*(person. 12 (#*en+er)) == 88 13 re(urn [n'%""5en+er $us( ,e 8$a%e8 or 8/e$a%e8"& '/ person.(#*en+er) 9= 14 8$a%e8 :: 15 person.(#*en+er) 9= 16 8/e$a%e8 17 18 '+ = 'nser()person.(person.(#na$e)"person.(#,'r(-+a(e)"person. 16 (#*en+er)"person.(#('(%e)) 2; [ne.)person.(person.(#na$e)"person.(#,'r(-+a(e)"person. 21 (#*en+er)"person.(#('(%e)"'+)"n'%& 22 } 23 24 *e()ne.)person = -> { 25 -an+%e)resu%(.(a++)person.(rea+)person)/ro$)user.())" 26 ->(person) { 27 pu(s "7u00ess/u%%1 a++e+ person #{person.(#'+)}" person }" ->(error)$essa*e) { pu(s "<ro,%e$# #{error)$essa*e}" *e()ne.)person.() } ) }

is a bit noisier, due to the synta0 of getting an attribute, but we can now add new fields $ery easily and &eep things structured"
a++)person

"bject#orientation is just functions


+e can also add deri$ed fields" #uppose we want a saluation for the person that uses their title2 +e can ma&e that an attribute of the person'
1 ne.)person = ->(na$e",'r(-+a(e"*en+er"('(%e"'+) { 2 re(urn ->(a((r',u(e) { 3 re(urn '+ '/ a((r',u(e == #'+ 4 re(urn na$e '/ a((r',u(e == #na$e 5 re(urn ,'r(-+a(e '/ a((r',u(e == #,'r(-+a(e 6 re(urn *en+er '/ a((r',u(e == #*en+er 7 re(urn ('(%e '/ a((r',u(e == #('(%e 8 '/ a((r',u(e == #sa%u(a('on 6 '/ 7(r'n*(('(%e) == 88 1; re(urn na$e 11 e%se 12 re(urn ('(%e A " " A na$e 13 en+ 14 en+ 15 n'% 16 } 17 }

-ec&, we can create full(on 44(style methods if we wanted to'


1 2 3 4 5 6 7 8 6 1; 11 12 13 14 15 16 17 18 16 2; 21 22 23 24 ne.)person = ->(na$e",'r(-+a(e"*en+er"('(%e"'+) { re(urn ->(a((r',u(e) { re(urn '+ '/ a((r',u(e == #'+ re(urn na$e '/ a((r',u(e == #na$e re(urn ,'r(-+a(e '/ a((r',u(e == #,'r(-+a(e re(urn *en+er '/ a((r',u(e == #*en+er re(urn ('(%e '/ a((r',u(e == #('(%e '/ a((r',u(e == #sa%u(a('on '/ 7(r'n*(('(%e) == 88 re(urn na$e e%se re(urn ('(%e A " " A na$e en+ e%s'/ a((r',u(e == #up+a(e up+a(e)person.(na$e",'r(-+a(e"*en+er"('(%e"'+) e%s'/ a((r',u(e == #+es(ro1 +e%e(e)person.('+) en+ n'% } } so$e)person.(#up+a(e) so$e)person.(#+es(ro1)

+hile we*re at it, let*s add inheritance< #uppose we ha$e an employee that is a person, but with an employee id number'
1 ne.)e$p%o1ee = ->(na$e",'r(-+a(e"*en+er"('(%e"e$p%o1ee)'+)nu$,er"'+) { 2 person = ne.)person.(na$e",'r(-+a(e"*en+er"('(%e"'+) 3 re(urn ->(a((r',u(e) { 4 re(urn e$p%o1ee)'+)nu$,er '/ a((r',u(e == #e$p%o1ee)'+)nu$,er

5 re(urn person.(a((r',u(e) 6 } 7}

+e*$e created classes, objects, and inheritance, all with just functions, and in just a few lines of code" !n a sense, an object in an 44 language is a set of functions that ha$e access to a shared set of data" !t*s not hard to see why adding an object system to a functional language is considered tri$ial by those &noweldgable in functional languages" !t*s certainly a lot easier than adding functions to an object(oriented language< Although the synta0 for accessing attributes is a bit clun&y, !*m not feeling a ton of pain by not ha$ing classes" ?lasses seem almost li&e syntactic sugar at this point, rather than some radical concept" 4ne thing that seems problematic is mutation" )oo& at how $erbose a++)person is" !t calls 'nser()person to put our person into the database, and gets an !C bac&" +e then ha$e to create an entirely new person just to set the !C" !n classic 44, we*d just do person.'+ = '+" !s mutable state what*s nice about this construct2 !*d argue that its compactness is what*s nice, and the fact that this compactness is implemented $ia mutable state is just incidental" ,nless we are in a se$erely memory(star$ed en$ironment, with terrible garbage collection, we aren*t going to be concerned about ma&ing new objects" +e are going to be annoyed by the needless repetition of building new objects from scratch" #ince we already &now how to add functions to our, er, function, let*s add one to bring bac& this compact synta0"
1 ne.)person = ->(na$e",'r(-+a(e"*en+er"('(%e"'+=n'%) { 2 re(urn ->(a((r',u(e"*ar*s) { 3 re(urn '+ '/ a((r',u(e == #'+ 4 re(urn na$e '/ a((r',u(e == #na$e 5 re(urn ,'r(-+a(e '/ a((r',u(e == #,'r(-+a(e 6 re(urn *en+er '/ a((r',u(e == #*en+er 7 re(urn ('(%e '/ a((r',u(e == #('(%e 8 '/ a((r',u(e == #sa%u(a('on 6 '/ 7(r'n*(('(%e) == 88 1; re(urn na$e 11 e%se 12 re(urn ('(%e A " " A na$e 13 en+ 14 en+ 15 16 '/ a((r',u(e == #.'(-)'+ # B=== 17 re(urn ne.)person.(na$e",'r(-+a(e"*en+er"('(%e"ar*s[;&) 18 en+ 16 2; n'% 21 } 22 }

3ow, a++)person is e$en simpler'


1 a++)person = ->(person) { 2 re(urn [n'%""2a$e 's requ're+"& '/ 7(r'n*(person.

3 4 5 6 7 8 6 1;

(#na$e)) == 88 re(urn [n'%""4'r(-+a(e 's requ're+"& '/ 7(r'n*(person. (#,'r(-+a(e)) == 88 re(urn [n'%""5en+er 's requ're+"& '/ 7(r'n*(person. (#*en+er)) == 88 re(urn [n'%""5en+er $us( ,e 8$a%e8 or 8/e$a%e8"& '/ person.(#*en+er) 9= 8$a%e8 :: person.(#*en+er) 9= 8/e$a%e8 '+ = 'nser()person.(person.(#na$e)"person.(#,'r(-+a(e)"person. (#*en+er)"person.(#('(%e)) [person.(#.'(-)'+"'+)"n'%& # B==== }

!t*s not Auite as clean as person.'+ = '+, but it*s terse enough that it*s still readable, and the code is better for it"

$amespaces are just functions


+hat !*m really missing is namespaces" !f you*$e done any ? programming, you &now that your code becomes littered with functions that ha$e comple0 prefi0es to a$oid name(clashes" +e could certainly do that here, but it would be nice to ha$e proper namespacing, li&e we get $ia modules in Ruby or object literals in Ja$a#cript" +e*d li&e to add this without adding a feature to our language" The simplest way to do that is to implement some sort of map" +e can already get e0plicit attributes of a data structure, so we just need a more generic way to do so" ?urrently, the only data structure we ha$e is an array, and we don*t ha$e methods, since we don*t ha$e classes" The arrays we ha$e are really tuples, and the only general operations we ha$e are the ability to e0tract data from them" .or e0ample'
1 /'rs( = ->((/"*res()) { / } # or s-ou%+ C na$e (-'s 0ar3 #) 2 res( = ->((/"*res()) { res( }

+e can model a map as a list, by treating it as a list with three entires' the &ey, the $alue, and the rest of the map" )et*s a$oid the 544 style6 of ma&ing 5methods6 and just &eep it pureful functional'
1 2 3 4 5 6 7 8 6 e$p(1)$ap = [& a++ = ->($ap"De1"!a%ue) { [De1"!a%ue"$ap& } *e( = ->($ap"De1) { re(urn n'% '/ $ap == n'% re(urn $ap[1& '/ $ap[;& == De1 re(urn *e(.($ap[2&"De1) }

+e can use it li&e so'


1 $ap = a++.(e$p(1)$ap"#/oo"#,ar) 2 $ap = a++.($ap"#,a?"#quux) 3 *e(.($ap"#/oo) # => #,ar

10

4 *e(.($ap"#,a?) # => #quux 5 *e(.($ap"#,%a-) # => n'%

This is enough to namepsace things'


peop%e = a++.(e$p(1)$ap "#'nser( "'nser()person) peop%e = a++.(peop%e "#up+a(e "up+a(e)person) peop%e = a++.(peop%e "#+e%e(e "+e%e(e)person) 1 peop%e = a++.(peop%e "#/e(0- "/e(0-)person) 2 peop%e = a++.(peop%e "#ne. "ne.)person) 3 4 a++)person = ->(person) { 5 re(urn [n'%""2a$e 's requ're+"& 6 (#na$e)) == 88 7 re(urn [n'%""4'r(-+a(e 's requ're+"& 8 (#,'r(-+a(e)) == 88 6 re(urn [n'%""5en+er 's requ're+"& 1; (#*en+er)) == 88 11 re(urn [n'%""5en+er $us( ,e 8$a%e8 or 8/e$a%e8"& 12 8$a%e8 :: 13 14 8/e$a%e8 15 16 '+ = *e((peop%e"#'nser().(person.(#na$e)" 17 person.(#,'r(-+a(e)" 18 person.(#*en+er)" 16 person.(#('(%e)) 2; } [*e((peop%e"#ne.).(#.'(-)'+"'+)"n'%&

'/ 7(r'n*(person. '/ 7(r'n*(person. '/ 7(r'n*(person. '/ person.(#*en+er) 9= person.(#*en+er) 9=

+e could certainly replace our ne.)person implementation with a map, but it*s nice to ha$e an e0plicit list of attributes that we support, so we*ll lea$e ne.)person as(is" 4ne last bit of magic" 'n0%u+e is a nice feature of Ruby9 it lets us bring modules into scope to a$oid using the namespace" ?an we do that here2 +e can get close'
1 2 3 4 5 6 7 8 6 1; 11 12 13 14 15 16 17 18 16 2; 'n0%u+e)na$espa0e = ->(na$espa0e"0o+e) { 0o+e.(->(De1) { *e((na$espa0e"De1) }) } a++)person = ->(person) { re(urn [n'%""2a$e 's requ're+"& (#na$e)) == 88 re(urn [n'%""4'r(-+a(e 's requ're+"& (#,'r(-+a(e)) == 88 re(urn [n'%""5en+er 's requ're+"& (#*en+er)) == 88 re(urn [n'%""5en+er $us( ,e 8$a%e8 or 8/e$a%e8"& 8$a%e8 :: 8/e$a%e8 'n0%u+e)na$espa0e(peop%e" ->()) { '+ = )(#'nser().(person.(#na$e)" person.(#,'r(-+a(e)" person.(#*en+er)" person.(#('(%e)) '/ 7(r'n*(person. '/ 7(r'n*(person. '/ 7(r'n*(person. '/ person.(#*en+er) 9= person.(#*en+er) 9=

11

} }

[)(#ne.).(#.'(-)'+"'+)"n'%&

4B, this might be o$er the top, but it*s fairly interesting to thin& of something li&e 'n0%u+e as just a way to 5type less stuff6, and that we can achie$e a similar reduction in 5typing stuff6 by just using functions"

What have %e learned&


+ith just a few basic language constructs, we can create a fairly usable programming language" +e can create bona(fide types, namespaces, and e$en do object(oriented programming, without any e0plicit support for these features" And we can do so in more or less the same amount of code that would be reAuired by using Ruby*s built(in support" The synta0 is slightly $erbose compared to the full(blown Ruby eAui$alent, but it*s not that bad" +e could write real code using this degenerate form of Ruby, and it wouldn*t be too bad" Coes this help us in our e$eryday wor&2 ! thin& this is a lesson in simplicity" Ruby is fraught with C#)s, abused synta0, and meta(programming, yet we*$e just been able to accomplish a lot without e$en using classes< erhaps the problem you ha$e in front of you can be sol$ed by something simple2 erhaps you don*t need anything fancy, but can rely on the more straightforward parts of your language" osted by Ca$id =ryant ?opeland Jul 17th, 2012

12

Potrebbero piacerti anche