On Tue, 2012-01-17 at 21:46 +0100, Dennis Haupt wrote:
> after the "wtf"s have worn off a bit, go on reading.
> imagine a simple problem: you have a collection of numbers and you have
> to write a function which collects all the numbers that are contained
> uneven times. for example, for a collection (1,2,3,2,3,4) the correct
> result is (1,4)
> 
> ask a child:
> "you just make pairs and return the leftovers"
> 
> ask a non-child human:
> "count how often a number is in the list and pick the ones that are
> contained uneven times"
> 
> i also asked some oo programmers. one of the answers involved a
> multimap, building it, iterating over it again and putting the result in
> a new list. one answer was "i don't see any purpose in this".
> there was no FP coder around at that time.
> 
> i've noticed this since i started to work as a programmer 10 years ago.
> programmers in general are supposed to be good at finding simple
> solutions, but my experience is: they are not. on the contrary, many
> suffer from their individual tunnel visions without being aware of it.
> to a hammer, everything looks like a nail.
> 
> i have walked a different path. when i was a child, i wondered: had i
> been born in a different country having different friends and parents,
> how would i be like? would i believe in a different god? would i like
> different music? i decided to try to be as independent from outside
> condition as possible, so i needed something as close to "the absolute
> truth" as possible and base my decisions on that. that absolute truth
> was nothing other than logic itself. it would always give the same
> answer, given the same input. it would never lead me to contradictions.
> it could not answer everything, but if it could, the answer always
> turned out to be correct - if testable. you could say logic is my
> personal tunnel through which i see everything, but i have yet to find a
> better one.
> i never care about how many people agree or disagree with me. if logic
> says "it's true", it just is.
> 
> when solving a problem, i always try to find the most simple and elegant
> solution thinkable in a grand scheme. i'm pretty demanding here. i
> barely accept my own code if it doesn't fit exactly to the problem it is
> supposed to solve :) now back to the topic.
> when writing code, i try to follow a few simple rules as good as possible:
> * no one should every have to read the code to figure out how it works.
> it should always be enough to take a look at what it does, not how.
> * when changing code, everything that is not in the current mental scope
> should not have any connection to the code that is being changed. it
> should always be possible to ignore everything else without breaking it
> by accident.
> * use the highest abstraction level that still makes sense.
> * not applicable to clojure, but: type your code well. well typed code
> is easy to analyse and hard to use wrong. if you're working with tables
> and define a rowint and a colint-type, you would never be able to
> confuse them. if you had a nonzeroint and a zeroint-type, you would
> never accidently divide by zero. if you are accessing an array that is
> zero-based, there should be a zerobasedindexint. otherwise, there should
> be a onebasedindexint.
> * if you need something more than once, there should be an easily
> useable and accessible function for it.
> * write code in such a way that it cannot go wrong :)
> * solve problems once (and for all) with structure, not with flat logic
> at many points. if a solution is too simple, its users will have to make
> up for it by adding logic on the outside.
> 
> this email didn't really have a purpose, i just didn't know a better
> place to post this.
> 

Unfortunately you are reasoning "in the small". Real systems have very
complex webs where some particular piece of code hangs. The code depends
on a LOT of other factors. I have attached a "simple" piece of algebra
code that is REALLY strongly typed and implements Algebraic Function
Fields in Axiom. It does an amazing amount of work in a relatively
trivial bit of code. It implements perfectly clear algorithms and was
written by a world-class computational mathematician. 

I have other examples from my Magnus project in the area of infinite
group theory. A few lines of C++ code is all you need to write to 
implement fundamental algorithms. The code is perfectly clear to the
author. It correctly and efficiently implements the ideas. I won't
bother with an example but, trust me, the code is as opaque as a brick.

There is the "good code meme" that all you need to do is use clear
variable names, properly indented code, and strong typing and *poof*,
the code is easy to understand. This meme is widespread and wrong.
It isn't "wrong" in the sense of good coding but it is being 
mis-applied, trying to cover human to human communication.

The problem is that "understanding" is based on communicating *ideas*
between people. Code is intended to communicate *actions* to a 
machine. These are very different goals and require different
technologies. Code cannot communicate "why" it was written.

Any real program that a company creates to solve a real problem
is going to be a large, complex web of technologies and *ideas*.
Once the original authors leave the project, the *ideas* are lost.
We need to capture the *ideas* as well as the code. My particular
solution is the "literate programming meme". Your results might
vary but the "good code meme" is not the solution.

tl;dr the "good code meme" is not enough; 
      use the "literate programming meme"

Tim Daly


-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
AlgebraicFunctionField(F, UP, UPUP, modulus): Exports == Impl where
  F      : Field
  UP     : UnivariatePolynomialCategory F
  UPUP   : UnivariatePolynomialCategory Fraction UP
  modulus: UPUP

  N   ==> NonNegativeInteger
  Z   ==> Integer
  RF  ==> Fraction UP
  QF  ==> Fraction UPUP
  UP2 ==> SparseUnivariatePolynomial UP
  SAE ==> SimpleAlgebraicExtension(RF, UPUP, modulus)
  INIT ==> if (deref brandNew?) then startUp false

  Exports ==> FunctionFieldCategory(F, UP, UPUP) with
    knownInfBasis: N -> Void
        ++ knownInfBasis(n) is not documented

  Impl ==> SAE add
    import ChangeOfVariable(F, UP, UPUP)
    import InnerCommonDenominator(UP, RF, Vector UP, Vector RF)
    import MatrixCommonDenominator(UP, RF)
    import UnivariatePolynomialCategoryFunctions2(RF, UPUP, UP, UP2)

    startUp    : Boolean -> Void
    vect       : Matrix RF -> Vector $
    getInfBasis: () -> Void

    brandNew?:Reference(Boolean) := ref true
    infBr?:Reference(Boolean) := ref true
    discPoly:Reference(RF) := ref 0
    n  := degree modulus
    n1 := (n - 1)::N
    ibasis:Matrix(RF)     := zero(n, n)
    invibasis:Matrix(RF)  := copy ibasis
    infbasis:Matrix(RF)   := copy ibasis
    invinfbasis:Matrix(RF):= copy ibasis

    branchPointAtInfinity?()   == (INIT; infBr?())
    discriminant()             == (INIT; discPoly())
    integralBasis()            == (INIT; vect ibasis)
    integralBasisAtInfinity()  == (INIT; vect infbasis)
    integralMatrix()           == (INIT; ibasis)
    inverseIntegralMatrix()    == (INIT; invibasis)
    integralMatrixAtInfinity() == (INIT; infbasis)
    branchPoint?(a:F)          == zero?((retract(discriminant())@UP) a)
    definingPolynomial()       == modulus
    inverseIntegralMatrixAtInfinity() == (INIT; invinfbasis)

    vect m ==
      [represents row(m, i) for i in minRowIndex m .. maxRowIndex m]

    integralCoordinates f ==
      splitDenominator(coordinates(f) * inverseIntegralMatrix())

    knownInfBasis d ==
      if deref brandNew? then
        alpha := [monomial(1, d * i)$UP :: RF for i in 0..n1]$Vector(RF)
        ib := diagonalMatrix
          [inv qelt(alpha, i) for i in minIndex alpha .. maxIndex alpha]
        invib := diagonalMatrix alpha
        for i in minRowIndex ib .. maxRowIndex ib repeat
          for j in minColIndex ib .. maxColIndex ib repeat
            infbasis(i, j)    := qelt(ib, i, j)
            invinfbasis(i, j) := invib(i, j)
      void

    getInfBasis() ==
      x           := inv(monomial(1, 1)$UP :: RF)
      invmod      := map(s +-> s(x), modulus)
      r           := mkIntegral invmod
      degree(r.poly) ^= n => error "Should not happen"
      ninvmod:UP2 := map(s +-> retract(s)@UP, r.poly)
      alpha       := [(r.coef ** i) x for i in 0..n1]$Vector(RF)
      invalpha := [inv qelt(alpha, i)
                   for i in minIndex alpha .. maxIndex alpha]$Vector(RF)
      invib       := integralBasis()$FunctionFieldIntegralBasis(UP, UP2,
                             SimpleAlgebraicExtension(UP, UP2, ninvmod))
      for i in minRowIndex ibasis .. maxRowIndex ibasis repeat
        for j in minColIndex ibasis .. maxColIndex ibasis repeat
          infbasis(i, j)    := ((invib.basis)(i,j) / invib.basisDen) x
          invinfbasis(i, j) := ((invib.basisInv) (i, j)) x
      ib2    := infbasis * diagonalMatrix alpha
      invib2 := diagonalMatrix(invalpha) * invinfbasis
      for i in minRowIndex ib2 .. maxRowIndex ib2 repeat
        for j in minColIndex ibasis .. maxColIndex ibasis repeat
          infbasis(i, j)    := qelt(ib2, i, j)
          invinfbasis(i, j) := invib2(i, j)
      void

    startUp b ==
      brandNew?() := b
      nmod:UP2    := map(retract, modulus)
      ib          := integralBasis()$FunctionFieldIntegralBasis(UP, UP2,
                                SimpleAlgebraicExtension(UP, UP2, nmod))
      for i in minRowIndex ibasis .. maxRowIndex ibasis repeat
        for j in minColIndex ibasis .. maxColIndex ibasis repeat
          qsetelt_!(ibasis, i, j, (ib.basis)(i, j) / ib.basisDen)
          invibasis(i, j) := ((ib.basisInv) (i, j))::RF
      if zero?(infbasis(minRowIndex infbasis, minColIndex infbasis))
        then getInfBasis()
      ib2    := coordinates normalizeAtInfinity vect ibasis
      invib2 := inverse(ib2)::Matrix(RF)
      for i in minRowIndex ib2 .. maxRowIndex ib2 repeat
        for j in minColIndex ib2 .. maxColIndex ib2 repeat
          ibasis(i, j)    := qelt(ib2, i, j)
          invibasis(i, j) := invib2(i, j)
      dsc  := resultant(modulus, differentiate modulus)
      dsc0 := dsc * determinant(infbasis) ** 2
      degree(numer dsc0) > degree(denom dsc0) =>error "Shouldn't happen"
      infBr?() := degree(numer dsc0) < degree(denom dsc0)
      dsc := dsc * determinant(ibasis) ** 2
      discPoly() := primitivePart(numer dsc) / denom(dsc)
      void

    integralDerivationMatrix d ==
      w := integralBasis()
      splitDenominator(coordinates([differentiate(w.i, d)
          for i in minIndex w .. maxIndex w]$Vector($))
               * inverseIntegralMatrix())

    integralRepresents(v, d) ==
      represents(coordinates(represents(v, d)) * integralMatrix())

    branchPoint?(p:UP) ==
      INIT
      (r:=retractIfCan(p)@Union(F,"failed")) case F =>branchPoint?(r::F)
      not ground? gcd(retract(discriminant())@UP, p)

Reply via email to