[HN Gopher] The evolution of a Scheme programmer (2020)
       ___________________________________________________________________
        
       The evolution of a Scheme programmer (2020)
        
       Author : ducktective
       Score  : 126 points
       Date   : 2024-05-15 18:33 UTC (2 days ago)
        
 (HTM) web link (erkin.party)
 (TXT) w3m dump (erkin.party)
        
       | shric wrote:
       | (2020)
        
         | NackerHughes wrote:
         | Thanks for the heads-up. I was almost about to be misinformed
         | by an outdated, no-longer-relevant article about a language
         | that's been more or less the same for the last few decades.
        
       | mark_l_watson wrote:
       | That was a tasty read! The Y-combinator implementation gave me a
       | slight headache, but I averted my eyes and all was OK.
       | 
       | Scheme as a language and from a clean/simplicity point of view
       | has always appealed to me, but I have mostly used Common Lisp
       | because I have so much legacy code.
        
         | anonzzzies wrote:
         | I use Scheme (these days more Racket or Shen) for doing
         | 'language experiments' but if anything gets to production,
         | it'll be CL. Like you said, legacy code, but also; it's just
         | more practical. You give up some elegance, but in exchange you
         | get something which you can build practically anything in, in
         | modern times.
        
       | roenxi wrote:
       | Isn't this missing the joke? The Haskell article code ended with
       | the "Tenured Professor" implementation (`fac n = product [1..n]`)
       | as a nod to the old trope of not needing to implement anything.
       | This article ends with `(define factorial (dynamic-require
       | 'math/number-theory 'factorial (lambda () lambda (n) (error
       | 'factorial "Cannot import library: ~a" 'math/number-theory)))))`.
       | 
       | Surely there is a lazier implementation than a define and 2x
       | lambdas to use a library function. Although I did get a chuckle
       | about not feeling a need to add brackets to the Scheme example.
        
         | Y_Y wrote:
         | But the extra code is for handling the error, you could just
         | `require` it and be done if you want to assume the library is
         | available.
        
         | shawn_w wrote:
         | Normal Racketeers would just `(require math/number-theory)` and
         | be done. No idea why they're loading the module at runtime like
         | that instead, unless it's a deliberately over complicated joke
         | solution like some of the others. Which would as you noted miss
         | the point of the original.
        
         | qart wrote:
         | Thankfully, the tenured professor version works in Julia like
         | so:                   prod(1:63)
         | 
         | For bigger numbers,                   factorial(n) = 1:n .|>
         | BigInt |> prod
        
           | soegaard wrote:
           | It's not the best algorithm for computing factorials though.
           | A better strategy is to pair small and large factors together
           | in order to avoid bignum computations (or to reduce the size
           | og bignum computations).
           | 
           | https://people.eecs.berkeley.edu/~fateman/papers/factorial.p.
           | ..
           | 
           | Also: It's more fun to use the name `fact` in benchmarks...
        
       | lispm wrote:
       | We might add an evolution of the display of parentheses...
       | 
       | B&W ()
       | 
       | B&W and optional bold ()
       | 
       | B&W and optional bold/larger ()
       | 
       | Full color rainbow parentheses.
       | 
       | B&W ()
       | 
       | One or two of the Lisp editors I use also support rainbow
       | parentheses, but after an initial phase of experimentation, I
       | have disabled that feature.
        
         | mckn1ght wrote:
         | This is the first time I've ever seen rainbow parentheses, it
         | seems like such a useful thing. What wound up leading you back
         | to B&W?
        
           | medo-bear wrote:
           | Probably because you get used to them and different colours
           | become distracting. Highlighting of active parentheses is
           | more useful
        
           | lispm wrote:
           | Too much visual clutter with very little actual benefit. I
           | found that I don't need/want to find matching parentheses by
           | a mix of position AND color.
        
             | aredox wrote:
             | Using an actual rainbow seems a bad idea. I will try to set
             | up a Viridis (or similar perceptually continuous) color
             | scheme next time I dabble in lisps.
        
         | dgb23 wrote:
         | I like my parens in a muted color with the currently active one
         | highlighted.
         | 
         | Rainbow colors are extremely distracting and overloading to me.
        
         | phforms wrote:
         | I also came back to B&W because I've noticed that with
         | structural editing I think more in terms of s-expressions than
         | parentheses and distinguishing them isn't that much of a
         | problem anymore.
        
         | roenxi wrote:
         | Golden opportunity to slip an "animated" option in there. I'm
         | pretty sure there are editors with a little jump to the other
         | side of a form to show where it is.
        
           | lispm wrote:
           | In Zmacs on the Lisp Machine, the s-expression under the
           | mouse cursor gets an outline. This is updated while the mouse
           | is moving.
        
         | ReleaseCandidat wrote:
         | I use these colours for rainbow brackets in all other languages
         | https://github.com/Release-Candidate/vscode-scheme-repl/blob...
         | but for LISPs I dimmed them (their saturation) down for normal
         | editing (when not "recording" animations/videos).
        
       | oersted wrote:
       | Zen of Python
       | 
       | > There should be one-- and preferably only one --obvious way to
       | do it.
       | 
       | > Although that way may not be obvious at first unless you're
       | Dutch.
        
         | tasuki wrote:
         | What is the preferred way to write factorial in Python?
        
           | oersted wrote:
           | I'd say the most idiomatic way is to use a simple for-loop:
           | 
           | def factorial(n):                   result = 1
           | for i in range(2, n + 1):                  result *= i
           | return result
           | 
           | The recursive version would be fine as well, but I would say
           | it's less idiomatic in Python, and less efficient.
           | 
           | def factorial(n):                   if n == 0 or n == 1:
           | return 1              else:                  return n *
           | factorial(n - 1)
           | 
           | Beyond that, I cannot think of any obvious way to do this
           | (without getting unreasonably convoluted).
           | 
           | In my opinion, it's great that there are such few options to
           | express this operation, and that both cases are eminently
           | readable.
        
             | Y_Y wrote:
             | > In my opinion, it's great that there are such few options
             | to express this operation, and that both cases are
             | eminently readable.
             | 
             | There are lots of ways to do this in Python. That's not
             | only fine, it's inevitable.
             | 
             | Here's a burnt-out PhD level one using highly pythonic
             | paradigms:                   def fac(n):
             | return eval("*".join(str(1+a) for a in range(n))
        
               | oersted wrote:
               | I have never seen a codebase that uses eval, it's
               | considered more toxic than goto.
               | 
               | Of course there are more ways, but I would definitely
               | qualify this as "unreasonably convoluted".
        
               | Y_Y wrote:
               | > it's considered more toxic then goto
               | 
               | Again I ask, considered by who? This is the kind of
               | language that wikipedians would call "weasel words". Of
               | course you shouldn't misuse eval or goto, and it's fine
               | to have a rule of thumb to discourage it.
               | 
               | What's not fine, and strikes me as no better than
               | superstition, is to make vague "everybody knows"-type
               | statements that convey almost no information. If it's bad
               | you should be able to say (or provide reference to)
               | specifically how it's bad and what convinced you this was
               | the case.
               | 
               | And yes, I deliberately wrote a perverse function because
               | it was funny, but if you did a real fold instead of my
               | fake one with strings then it wouldn't be so perverse.
               | And wasn't it more pythonic to use a generator?
               | 
               | Not embracing Perl's tmtowtdi is fine, but until you
               | solve the halting problem some people will write "x+x"
               | and some will write "2*x" and some will write "x<<2" and
               | those can't sensibly be unified.
        
             | tasuki wrote:
             | I find both Haskell's `fac n = product [1..n]` and F# `let
             | factorial n = [1..n] |> List.reduce ( _)` more readable and
             | easier to comprehend. You could say using `product` is
             | cheating, but `fac n = fold (_ ) [1..n]` definitely isn't!
             | 
             | Your idiomatic Python version uses three variables, two of
             | which mutate. I'm a simple man - go easy on me please! The
             | `n + 1` took me a while to figure out too.
        
           | Y_Y wrote:
           | Preferred by who? `math.factorial` is a good start.
        
             | oersted wrote:
             | Obviously preferable, it was just to illustrate the "one
             | obvious way of doing something".
        
               | tasuki wrote:
               | I'm delighted to see there are actually several ways and
               | you good people are discussing them.
               | 
               | This is _exactly_ why I asked the question!
        
           | asplake wrote:
           | How about:                 from math import prod # from
           | Python 3.8 onwards              def factorial(n):
           | return prod(range(n+1))
        
             | oersted wrote:
             | Definitely preferable, wasn't aware of it.
        
             | gshubert17 wrote:
             | Since the first element in range() will be 0, should the
             | last line be ...?                   return
             | prod(1,range(n+1))
        
               | asplake wrote:
               | Oops yes! And unfortunately it still doesn't work for n=0
               | 
               | Edit: prod(range(1, n+1)) does work
        
               | gshubert17 wrote:
               | My oops too. The extra 1 argument belongs with range, as
               | you have it here.
        
       | DeathArrow wrote:
       | I like how in F# things are simpler:
       | 
       | let factorial n = [1..n] |> List.reduce (*)
        
         | pona-a wrote:
         | (defn fac [n] (->> (range 1 (inc n)) (reduce *))) or in
         | Clojure.
        
       | aredox wrote:
       | Has someone tried to evaluate all the solutions in terms of
       | memory/CPU usage? Or do all the differences end up optimised
       | away?
        
       | 0x445442 wrote:
       | And then there's Raku; say [*] 1..$n
        
       ___________________________________________________________________
       (page generated 2024-05-17 23:01 UTC)