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)