Forget about what programming languages you know. What syntax do you
wish you could write in? (Names have been changed to protect the
innocent):
This?
function generate_fibonacci_sequence( $length ) {
for( $l = array(1,1), $i = 2, $x = 0; $i < $length; $i++ )
$l[] = $l[$x++] + $l[$x];
return $l;
}
What about this?
sub fibo
{
my ($n, $a, $b) = (shift, 0, 1);
($a, $b) = ($b, $a + $b) while $n
$a;
}
Getting better?
def fib(n):
if n < 2: return 1
else : return fib(n 1) + fib(n 2)
Hmm, isn't this going backwards?
(define fibo
(lambda (x)
(if (< x 2)
x
(+ (fibo ( x 1)) (fibo ( x 2))))))
Finally:
fib 0 = 0
fib 1 = 1
fib n = fib (n1) + fib (n2)
If the programming languages of the future don't look like the last
example, I think we've failed. Programming languages are notation for
describing solutions to problems. If that notation is verbose, clunky
or full of special cases it should be abandoned in favour of a more
concise notation. Real languages should look like pseudcode!
My suspicion is that in 50 years time we'll look back on the syntax of
today's programming languages and think them highly bizarre, like
looking back at Frege's
logic notation (pdf):
Syntax has come along way since the 1960s, but there's still room for
much improvement, looking at the widely used languages employed today.
The designers of many (not all) popular languages seem to forget that
syntax should be optimised for writing by humans, not for parsing by
machines.
/home ::
/haskell ::
permalink ::
rss
Haskell uses type inference, so explicit type declarations are rarely
required. This:
import Control.Monad.Fix
fibs = fix ((1:) . scanl (+) 1)
main = print (take 20 fibs)
Is just as good as:
import Control.Monad.Fix
fibs :: [Integer]
fibs = fix ((1:) . scanl (+) 1)
main :: IO ()
main = print (take 20 fibs)
Running this:
$ runhaskell A.hs
[1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765]
Now, once programs reach a decent size, the advantage of type
declarations appears: it functions as machine-checkable documentation.
So people new to your code can more quickly work out what the code is
doing.
Contrast this (real world) Haskell code from a network client:
accessorMS decompose f = withMS $ \s writer ->
let (t,k) = decompose s in f t (writer . k)
And with type annotations:
accessorMS :: (s -> (t, t -> s))
-> (t -> (t -> LB ()) -> LB a)
-> ModuleT s LB a
accessorMS decompose f = withMS $ \s writer ->
let (t,k) = decompose s in f t (writer . k)
So at least you know have some idea of what that code does.
There's an intuition here: type declarations are good, but they can be
mechanically inferred. Let's automate that then! Here's a quick script
I use every day. It just passes your top level declaration to ghci, and
asks it to infer the type. The resulting type signature is spliced back
in to your code:
#!/bin/sh
# input is a top level .hs decls
FILE=$*
DECL=`cat`
ID=`echo $DECL | sed 's/^\([^ ]*\).*/\1/'`
echo ":t $ID" | ghci -v0 -cpp -fglasgow-exts -w $FILE
echo $DECL
Save this as an executable shell file in your path. Now you can call
this from your editor. For example, from vim, you'd use the following
.vimrc:
:map ty :.!typeOf %
Hitting :ty while the cursor is positioned on top of a top level
declaration takes your code from:
writePS who x = withPS who (\_ writer -> writer x)
to this:
writePS :: forall p g. String -> Maybe p -> ModuleT (GlobalPrivate g p) LB ()
writePS who x = withPS who (\_ writer -> writer x)
I can't emphasise enough how useful this is. So, steal this code and
improve your productivity!
/home ::
/haskell ::
permalink ::
rss
IPI asked this how #haskell:
How can I mentally infer the type of the composition of these functions?
a :: [[Char]] -> [([Char],Int,Int)]
b :: [Int] -> [(Int,Bool)]
c :: [([Char],Int,Int)] -> ([Char],Int)
d :: [(Int,Bool)] -> [[Char]]
That is, what is the type of:
c . a . d . b
The first thing I'd do is rename the tricky types:
a :: [String] -> [(String,Int,Int)]
b :: [Int] -> [(Int,Bool)]
c :: [(String,Int,Int)] -> (String,Int)
d :: [(Int,Bool)] -> [String]
and then introduce some type synonyms:
type A = [String]
type B = [(String,Int,Int)]
type C = [(Int,Bool)]
Now we can rename the complex types to a more symbolic form:
a :: A -> B
b :: [Int] -> C
c :: B -> (String,Int)
d :: C -> A
and now we can see how to combine the pieces. It's a little jigsaw puzzle!
b :: [Int] -> C
so there's only one option to compose with b, namely
b :: [Int] -> C
d :: C -> A
composing these, and we hide the intermediate result:
d . b :: [Int] -> A
now, we need something that takes an A:
a :: A -> B
So the result of something composed with a, will be a value of type B:
a . d . b :: [Int] -> B
and finally, something that takes a B:
c :: B -> (String,Int)
leading to:
c . a . d . b :: [Int] -> (String,Int)
and we're done!
/home ::
/haskell ::
permalink ::
rss