Skip to content

Instantly share code, notes, and snippets.

@Kazark
Created July 2, 2020 22:20
Show Gist options
  • Save Kazark/e830f52d769976fb54fb35b80555ee80 to your computer and use it in GitHub Desktop.
Save Kazark/e830f52d769976fb54fb35b80555ee80 to your computer and use it in GitHub Desktop.
A playful implementation of square root in Guile, with a Haskell flavor
#| An obfuscated Haskell-style solution to exercise 1.7 from SICP in Guile.
|
| The basis of this solution is the idea: the numerical method for square root
| does not inherently have any notion of what is means for the solution to be
| "good enough"; that is an _orthogonal_ concern. The inherent idea, in its
| purest form, is a limit of the algorithm considered as a function of the
| number of iterations. Therefore, it would be pleasing in our implementation
| to divorce the idea of what "good enough" means from our expression of the
| algorithm. But this requires an infinite data structure, because if we do not
| have an infinite data structure, we must from the first think about when to
| stop!
|
| Secondly, how do we know if we are close, without knowing what the answer is?
| Assuming a monotonic behavior of the square root approximation w.r.t.
| iterations (which this algorithm exhibits) then without actually knowing the
| _precise_ answer, the derivative of our algorithm considered as a function of
| the number of iterations will tell us how much closer each iteration is
| getting us. We then have a way of knowing when we are close, because if our
| monotonic function is not changing much, we are by definition close.
|
| For our infinite data structure, we will use Haskell-style lists, a.k.a.
| streams: that is, either a delayed '(), or a delayed cons cell. Thus, the
| empty stream is
|
| (delay '())
|
| A stream that corresponds to (list 'foo 'bar 'baz) is
|
| (delay (: 'foo (delay (: 'bar (delay (: 'baz (delay '())))))))
|
| and so on.
|#
(import (ice-9 match)) ;; pattern matching is implemented in a library in Guile
#| Syntactic sugar/obfuscation |#
(define : cons)
(define :ΒΉ car)
(define :Β² cdr)
(define :Β² cdr)
(define =? eq?)
#| Second/cdr-biased functor map for cons cells |#
(define (:-map 𝑓 ΒΉ-Β²) (: (:ΒΉ ΒΉ-Β²) (𝑓 (:Β² ΒΉ-Β²))))
#| Anamorphism for streams. Conceptual type signature:
| unfold :: (b -> Maybe (a, b)) -> b -> Stream a
|#
(define (↑ 𝑓 π‘₯)
(delay (match (𝑓 π‘₯)
(#f '())
((π‘₯ . π‘₯') (: π‘₯ (↑ 𝑓 π‘₯'))))))
#| Functor map for a stream |#
(define (∞-map 𝑓 π‘₯π‘₯)
(delay (match (force π‘₯π‘₯)
(() '())
((π‘₯ . π‘₯π‘₯') (: (𝑓 π‘₯) (∞-map 𝑓 π‘₯π‘₯'))))))
#| Drop a given number of elements from the beginning of stream |#
(define (↓ i π‘₯π‘₯)
(if (=? i 0)
π‘₯π‘₯
(↓ (- i 1) (:Β² (force π‘₯π‘₯)))))
#| Pluck out an element from a stream, if you can find one that matches the
| predicate.
|#
(define (? ?' π‘₯π‘₯)
(match (force π‘₯π‘₯)
((π‘₯ . π‘₯π‘₯') (if (?' π‘₯) π‘₯ (? ?' π‘₯π‘₯')))))
#| Zip two streams into one, ending when either ends, if either does |#
(define (=>- π‘₯π‘₯ 𝑦𝑦)
(delay (match (: (force π‘₯π‘₯) (force 𝑦𝑦))
((() . _) '())
((_ . ()) '())
(((π‘₯ . π‘₯π‘₯') . (𝑦 . 𝑦𝑦')) (: (: π‘₯ 𝑦) (=>- π‘₯π‘₯' 𝑦𝑦'))))))
#| Average (mean) of two numbers |#
(define (avg π‘₯ 𝑦) (/ (+ π‘₯ 𝑦) 2))
#| One iteration of the square-root approximation method |#
(define (βˆšβ‰ˆ β‰ˆ π‘₯) (avg β‰ˆ (/ π‘₯ β‰ˆ)))
#| Comonad duplicate for cons cells |#
(define (:-dup π‘₯) (: (:ΒΉ π‘₯) π‘₯))
#| Apply a cons cell to a function |#
(define (ap-: 𝑓) (Ξ» (π‘₯-𝑦) (𝑓 (:ΒΉ π‘₯-𝑦) (:Β² π‘₯-𝑦))))
#| βˆšβ‰ˆ adapted for use with ↑ |#
(define (β†‘βˆšβ‰ˆ β‰ˆ π‘₯) (:-dup (: (βˆšβ‰ˆ β‰ˆ π‘₯) π‘₯)))
#| The square root algorithm considered as a function of iterations as the
| number of iterations approaches infinity, encoded as an infinite stream of
| approximations.
|#
(define (βˆžβˆšβ‰ˆ π‘₯) (↑ (ap-: β†‘βˆšβ‰ˆ) (: 1.0 π‘₯)))
#| Function composition operator |#
(define (∘ 𝑓 𝓰) (Ξ» (π‘₯) (𝑓 (𝓰 π‘₯))))
#| S-combinator from SKI calculus |#
(define (𝐒 𝑓 𝓰 π‘₯) (𝑓 π‘₯ (𝓰 π‘₯)))
#| The idea of being good enough, in the abstract, is being close to some
| tolerance. What this tolerance is, and what we are comparing it to, we don't
| specify here. Booyeah orthogonality.
|#
(define (Β±? Β±) (Ξ» (π‘₯) (> Β± (abs π‘₯))))
#| Take a derivative, in the sense of derivative calculus, of a function
| represented as a stream.
|#
(define (βˆ‚ 𝑓) (∞-map (ap-: -) (=>- (↓ 1 𝑓) 𝑓)))
#| Find an approximation for the square root of π‘₯ when the our answer starts to
| change less than the given tolerance. Example:
| > (√ 0.0000000001 2)
| 1.4142135623746899
|#
(define (√ Β± π‘₯) (:ΒΉ (? (∘ (Β±? Β±) :Β²) (𝐒 =>- βˆ‚ (βˆžβˆšβ‰ˆ π‘₯)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment