On Jul 4, 2014, at 2:42 AM, Matthias Felleisen wrote:

Thanks for the tips (comments below).

> On Jul 3, 2014, at 8:01 PM, John Clements wrote:
>> On Jul 3, 2014, at 8:52 AM, Brian Adkins <racketus...@lojic.com> wrote:
>>> Hi all:
>>> 
>>> I've recently begun learning Racket. After making some progress through 
>>> various books & tutorials, I took a shot at porting a non-trivial program I 
>>> had written in Haskell to help choose a lunch place for a group of 
>>> colleagues based on personal rankings of restaurants and individual history 
>>> of visiting restaurants.
>>> 
>>> The Haskell code was quite unpolished to begin with, so when you combine 
>>> that with my very limited Racket knowledge, the ported code has much 
>>> potential for improvement :) Both versions are in this gist:
>>> 
>>> https://gist.github.com/lojic/d45437453ccc8bcba196
>> 
>> Hmm, looks pretty good to me… except for the missing purpose statements on 
>> `rankrestaurants`, `rankUser`, and `rank`…
> 
> I would work hard to squeeze the program into 102 columns, and I might 
> eliminate local and let in favor of defined variables, thusly: 

I have noticed that it's been a bit of a challenge keeping my code from 
creeping too far to the right with Racket - just the nature of lisp I suppose. 
I typically have at least a couple Emacs windows open in split-screen which 
gives me at most 98 columns when mobile or 116 when at my desk, so I usually 
try to stay w/in 98 columns just to avoid a wrap.

Just out of curiosity, is "102" a personal preference, or is there another 
reason for not exceeding that?

> ;; completeRatings adds a default (User, 1.0) rating to
> ;; restaurants so each restaurant has a rating from every user
> (define (filledOut ratings) 
>  (for/list ([user users]) (or (assoc user ratings) (list user 1.0))))
> (define completeRatings
>  (for/list ([entry rawRatings])
>    (match entry [(list restaurant ratings) (list restaurant (filledOut 
> ratings))])))

I did consider whether some of my local functions should be at the top level. I 
generally favor explicit over implicit. The pros, for me, of promoting a 
function are:

* easier unit testing (or testing in the repl)
* being explicit about the data requirements, via args, of a function
* not creeping to the right so soon :)
* can be used elsewhere w/o refactoring if the need arises
* ?

The pros of local functions:

* explicitly communicating the function use scope
* more concise/readable code in *some* instances, e.g. w/ closures
* ?

The use of local functions did make testing the lunch code more difficult, but 
I think part of that was because I was simply porting and trying to match the 
Haskell code. IIRC I think I developed the Haskell code much more incrementally 
and tested the local functions via their outer function as I added them.

One concern w/ non-local functions is that, w/o a type checker, modifying a 
function signature requires finding all invocation instances and changing them. 
If a function is local, the scope is tiny. Maybe I'll find that proper use of 
modules will make finding/modifying function invocations easy enough for 
refactoring.

> I might also embed such things in a 'main' function. 
> 
> If there were a remote chance that the program would have to be used again, I 
> would probably add (module+ test (require rackunit)) and I'd add tests for 
> functions. 

Yes, rackunit is on my short list of things to look into. I usually have decent 
test coverage in my professional code, but this was just a diversionary hack 
for friends :)

*So* much to learn with Racket, but I'm having a blast! Sincere thanks to those 
who have brought Racket to where it is.



____________________
  Racket Users list:
  http://lists.racket-lang.org/users

Reply via email to