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