
While using Haskell, I often find myself writing convoluted constructions such as this: show_system = unlines . zipWith (\l ms -> "Eq" ++ show l ++ ": " ++ (concat $ intersperse " + " $ zipWith (\n x -> x ++ " x" ++ show n) [1..] (init ms)) ++ " = " ++ last ms ) [1..] . map (map (take 8 . show)) And people complain that *Perl* is bad? This function is quite obviously absurd. I mean, it works, but can *you* figure out what it does without running it? The question is, can anybody think of a better way to write this function? (And more generally, functions like it.)

Hi
show_system = unlines . zipWith (\l ms -> "Eq" ++ show l ++ ": " ++ (concat $ intersperse " + " $ zipWith (\n x -> x ++ " x" ++ show n) [1..] (init ms)) ++ " = " ++ last ms ) [1..] . map (map (take 8 . show))
And people complain that *Perl* is bad? This function is quite obviously absurd. I mean, it works, but can *you* figure out what it does without running it?
No. Can you say what the intention of this code is? Maybe a few examples? The type signature? That way I think people will be more able to give you hints. Generally, I find list comprehensions to be a good way out of general listy mess. Thanks Neil

Neil Mitchell wrote:
Hi
And people complain that *Perl* is bad? This function is quite obviously absurd. I mean, it works, but can *you* figure out what it does without running it?
No. Can you say what the intention of this code is? Maybe a few examples? The type signature? That way I think people will be more able to give you hints.
Type signature is show_system :: [[Double]] -> String It takes a matrix representing a system of equations, and pretty prints it. Unfortunately, doing complex formatting like that is... well, complex. The input is quite simple (it's a bunch of numbers), the output is quite simple (it's a neatly formatted string), but the process in the middle is... a mess. I'd like to find a more readable way of doing stuff like this. It's not just this specific function; any general hints would be good. ;-)

On 25/09/2007, Andrew Coppin
Type signature is
show_system :: [[Double]] -> String
It takes a matrix representing a system of equations, and pretty prints it. Unfortunately, doing complex formatting like that is... well, complex. The input is quite simple (it's a bunch of numbers), the output is quite simple (it's a neatly formatted string), but the process in the middle is... a mess. I'd like to find a more readable way of doing stuff like this. It's not just this specific function; any general hints would be good. ;-)
In this instance I would suggest: (1) Text.Printf (2) Pull out some of those things into separate functions with where/let clauses. If it's a matrix you should probably have something like
showMatrix = concatMap showRow
Since you'll be applying the same procedures to each line of digits. Cheers, D.

On Tue, Sep 25, 2007 at 10:31:34AM +0100, Dougal Stanton wrote:
On 25/09/2007, Andrew Coppin
wrote: In this instance I would suggest: (1) Text.Printf (2) Pull out some of those things into separate functions with where/let clauses.
If it's a matrix you should probably have something like
showMatrix = concatMap showRow
Since you'll be applying the same procedures to each line of digits.
Just to follow those sentiments, the version I knocked out quickly looked like: (It's not quite the same as the original function, I think I'm lacking a map (map (take 8)) on the first line). showSystems :: Show a => [[a]] -> String showSystems = unlines . zipWith showSystem [1..] where showSystem n as = "Eq" ++ (show n) ++ ": " ++ sum ++ " = " ++ val where sum = concat . intersperse " + " . zipWith showNum [1..] $ (init as) val = show . last $ as showNum n a = show a ++ " x" ++ show n Pointsfree and explicit lambda notation I find can be very concise in places, but make it quite hard to reuse or refactor code later - if you can't read it, make a function/variable with a useful name so you can later. Regards, T -- Tristan Allwood PhD Student Department of Computing Imperial College London

Tristan Allwood wrote:
Just to follow those sentiments, the version I knocked out quickly looked like: (It's not quite the same as the original function, I think I'm lacking a map (map (take 8)) on the first line).
showSystems :: Show a => [[a]] -> String showSystems = unlines . zipWith showSystem [1..] where showSystem n as = "Eq" ++ (show n) ++ ": " ++ sum ++ " = " ++ val where sum = concat . intersperse " + " . zipWith showNum [1..] $ (init as) val = show . last $ as showNum n a = show a ++ " x" ++ show n
I'm puzzled - do we have 2 seperate where clauses?
Pointsfree and explicit lambda notation I find can be very concise in places, but make it quite hard to reuse or refactor code later - if you can't read it, make a function/variable with a useful name so you can later.
Mmm, so just like every other programming language. Theoretically you could write a ten thousand line Pascal program on 1 line - but please don't! Hehe.

On Tue, Sep 25, 2007 at 10:53:55AM +0100, Andrew Coppin wrote:
Tristan Allwood wrote:
Just to follow those sentiments, the version I knocked out quickly looked like: (It's not quite the same as the original function, I think I'm lacking a map (map (take 8)) on the first line).
showSystems :: Show a => [[a]] -> String showSystems = unlines . zipWith showSystem [1..] where showSystem n as = "Eq" ++ (show n) ++ ": " ++ sum ++ " = " ++ val where sum = concat . intersperse " + " . zipWith showNum [1..] $ (init as) val = show . last $ as showNum n a = show a ++ " x" ++ show n
I'm puzzled - do we have 2 seperate where clauses?
Yes there are 2 and no they arn't seperate. The second where clause is nested and is a set of definitioins local to showSystem, and as such can use the bound values of n and as. (But it can also "see" any values bound by showSystems and its where clause (which in this case is just the definition of showSystem). Clear as mud? T -- Tristan Allwood PhD Student Department of Computing Imperial College London

Dougal Stanton wrote:
In this instance I would suggest:
(1) Text.Printf
You've got to be kidding... I went to all the trouble of learning a "scary logic programming language [sic]" just to avoid that damned printf() function! :-/
(2) Pull out some of those things into separate functions with where/let clauses.
If it's a matrix you should probably have something like
showMatrix = concatMap showRow
Since you'll be applying the same procedures to each line of digits.
Well, actually more like show_matrix = concat . zipWith show_row [1..] but yeah, this would probably simplify things somewhat.

On Sep 25, 2007, at 5:48 , Andrew Coppin wrote:
Dougal Stanton wrote:
In this instance I would suggest:
(1) Text.Printf
You've got to be kidding... I went to all the trouble of learning a "scary logic programming language [sic]" just to avoid that damned printf() function! :-/
Enh. :) On the other hand, I do wonder that nobody's written a combinator-based numeric formatting library. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

Brandon S. Allbery KF8NH wrote:
On Sep 25, 2007, at 5:48 , Andrew Coppin wrote:
You've got to be kidding... I went to all the trouble of learning a "scary logic programming language [sic]" just to avoid that damned printf() function! :-/
Enh. :) On the other hand, I do wonder that nobody's written a combinator-based numeric formatting library.
They probably have - it's probably based on Peano integers though. :-P More seriously, I have no idea how you'd implement this in Haskell. Presumably the standard show instance for Int, Double, etc. is in native C? You could probably reimplement it in Haskell for the integer case, but not for floating-point...

On Sep 25, 2007, at 5:56 , Andrew Coppin wrote:
More seriously, I have no idea how you'd implement this in Haskell. Presumably the standard show instance for Int, Double, etc. is in native C? You could probably reimplement it in Haskell for the integer case, but not for floating-point...
Actually, Text.Printf is pure Haskell. (Very *scary* Haskell: deep type hackery is needed to make it work. Don't try to understand PrintfType. :) -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

Brandon S. Allbery KF8NH wrote:
On Sep 25, 2007, at 5:56 , Andrew Coppin wrote:
More seriously, I have no idea how you'd implement this in Haskell. Presumably the standard show instance for Int, Double, etc. is in native C? You could probably reimplement it in Haskell for the integer case, but not for floating-point...
Actually, Text.Printf is pure Haskell. (Very *scary* Haskell: deep type hackery is needed to make it work. Don't try to understand PrintfType. :)
Forget PrintfType - I can't even understand the haddoc page yet! (printf performs I/O, yet it is outside the I/O monad. It seems to accept an arbitrary number of arguments, which is obviously impossible. It's *almost* as scary as the original. Although the original will crash your program is you use it wr... oh, so does this one. Cool.)

Hi
complex. The input is quite simple (it's a bunch of numbers), the output is quite simple (it's a neatly formatted string), but the process in the middle is... a mess. I'd like to find a more readable way of doing stuff like this. It's not just this specific function; any general hints would be good. ;-)
A nice auxiliary would help: showEqn :: Int -> [Double] -> String showEqn i vs = ... where (add,ans) = (init vs, last vs) Then you can half the complexity. There are probably a few useful functions that aren't in the standard libraries (consperse, joinWith etc) that you could make use of. You seem to be doing take 8 on the double - what if the double prints out more information than this as the result of show? 1000000000000000000 could end up a bit smaller. Thanks Neil

Neil Mitchell wrote:
Hi
A nice auxiliary would help:
showEqn :: Int -> [Double] -> String showEqn i vs = ... where (add,ans) = (init vs, last vs)
Then you can half the complexity. There are probably a few useful functions that aren't in the standard libraries (consperse, joinWith etc) that you could make use of.
OK, I'll give that a go... (Actually, at present it's an "augmented matrix", represented as [[Double]]. I'm thinking by making it a matrix and seperate vector, I could reduce the runtime quite significantly by making it much faster to get hold of the RHS.) BTW, one *extremely* common function that I've never seen mentioned anywhere is this one: map2 :: (a -> b) -> [[a]] -> [[b]] map2 f = map (map f) I cannot understand why this isn't already in the standard libraries...
You seem to be doing take 8 on the double - what if the double prints out more information than this as the result of show? 1000000000000000000 could end up a bit smaller.
The contents of the matrix are random numbers between 0 and 100. The problems happen if one of the numbers turns out to be something like "3.5847368473587e-27" or something. Then it will look many thousands of times BIGGER! ;-) Still, since Haskell seems to be devoid of any more advanced way of formatting numbers beyond low-level character jiggling...

On Sep 25, 2007, at 5:45 , Andrew Coppin wrote:
Still, since Haskell seems to be devoid of any more advanced way of formatting numbers beyond low-level character jiggling...
Text.Printf.printf is your friend. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On 2007-09-25, Andrew Coppin
BTW, one *extremely* common function that I've never seen mentioned anywhere is this one:
map2 :: (a -> b) -> [[a]] -> [[b]] map2 f = map (map f)
Because someone would have to think of a name for it, when (map . map) is likely to be clearer. -- Aaron Denney -><-

Aaron Denney wrote:
On 2007-09-25, Andrew Coppin
wrote: BTW, one *extremely* common function that I've never seen mentioned anywhere is this one:
map2 :: (a -> b) -> [[a]] -> [[b]] map2 f = map (map f)
Because someone would have to think of a name for it, when (map . map) is likely to be clearer.
OK, *now* I'm puzzled... Why does map . map type-check?

On 2007-09-25, Andrew Coppin
Aaron Denney wrote:
On 2007-09-25, Andrew Coppin
wrote: BTW, one *extremely* common function that I've never seen mentioned anywhere is this one:
map2 :: (a -> b) -> [[a]] -> [[b]] map2 f = map (map f)
Because someone would have to think of a name for it, when (map . map) is likely to be clearer.
OK, *now* I'm puzzled... Why does map . map type-check?
(map . map) = (.) map map (.) :: (a -> b) -> (b -> c) -> a -> c = (a -> b) -> (b -> c) -> (a -> c) The first two arguments of (.) are 1-argument functions. map :: (d -> e) -> [d] -> [e] = (d -> e) -> ([d] -> [e]) map is either a two argument function _or_ a function that takes one argument (a function) and returns a function. In this latter view, for the first argument, of (.), we need: a = d -> e b = [d] -> [e] And for the second we know b = [d] -> [e] so c = [[d]] -> [[e]] for everything to be consistent. It's much clearer when you think of map not as "running this function over this list", but rather "turning this function that operates on elements into a function that operates on lists". Doing that twice (by composing) turns a function that operates on elements into a function that operates on lists of lists. -- Aaron Denney -><-

Aaron Denney wrote:
On 2007-09-25, Andrew Coppin
wrote: OK, *now* I'm puzzled... Why does map . map type-check?
(map . map) = (.) map map
(.) :: (a -> b) -> (b -> c) -> a -> c = (a -> b) -> (b -> c) -> (a -> c)
The first two arguments of (.) are 1-argument functions.
map :: (d -> e) -> [d] -> [e] = (d -> e) -> ([d] -> [e])
map is either a two argument function _or_ a function that takes one argument (a function) and returns a function.
In this latter view, for the first argument, of (.), we need:
a = d -> e b = [d] -> [e]
And for the second we know b = [d] -> [e] so c = [[d]] -> [[e]]
for everything to be consistent.
It's much clearer when you think of map not as "running this function over this list", but rather "turning this function that operates on elements into a function that operates on lists". Doing that twice (by composing) turns a function that operates on elements into a function that operates on lists of lists.
I just found it rather surprising. Every time *I* try to compose with functions of more than 1 argument, the type checker complains. Specifically, suppose you have foo = f3 . f2 . f1 Assuming those are all 1-argument functions, it works great. But if f1 is a *two* argument function (like map is), the type checker refuses to allow it, and I have to rewrite it as foo x y = f3 $ f2 $ f1 x y which is really extremely annoying... I'm just curiose as to why the type checker won't let *me* do it, but it will let *you* do it. (Maybe it hates me?)

On 2007-09-25, Andrew Coppin
I just found it rather surprising. Every time *I* try to compose with functions of more than 1 argument, the type checker complains. Specifically, suppose you have
foo = f3 . f2 . f1
Assuming those are all 1-argument functions, it works great. But if f1 is a *two* argument function (like map is), the type checker refuses to allow it, and I have to rewrite it as
foo x y = f3 $ f2 $ f1 x y
which is really extremely annoying...
I'm just curiose as to why the type checker won't let *me* do it, but it will let *you* do it. (Maybe it hates me?)
Don't anthropomorphize computers. They hate it when you do that. I'm guessing the problem is probably incorrect parenthesizing. foo x y = f3 . f2 . f1 x y won't typecheck, but foo x y = (f3 . f2 . f1) x y should. Function application is the highest precedence, so the first definition is parsed as foo x y = f3 . f2 . (f1 x y) which will only type-check if f1 has 3 or more arguments, as (f1 x y) must be a function. The trickier parts are more than 1 argument functions as the first argument to (.). Are you sure your failed attempts weren't of this form? -- Aaron Denney -><-

Andrew Coppin
I just found it rather surprising. Every time *I* try to compose with functions of more than 1 argument, the type checker complains. Specifically, suppose you have
foo = f3 . f2 . f1
Assuming those are all 1-argument functions, it works great. But if f1 is a *two* argument function (like map is), the type checker refuses to allow it, and I have to rewrite it as
foo x y = f3 $ f2 $ f1 x y
Look at the type of (.).(.) which should tell you how to compose functions with more than one variable. Mind you, I don't think it improves readability. Dominic.

Dominic Steinitz schrieb:
Andrew Coppin
writes: I just found it rather surprising. Every time *I* try to compose with functions of more than 1 argument, the type checker complains. Specifically, suppose you have
foo = f3 . f2 . f1
Assuming those are all 1-argument functions, it works great. But if f1 is a *two* argument function (like map is), the type checker refuses to allow it, and I have to rewrite it as
foo x y = f3 $ f2 $ f1 x y
Look at the type of (.).(.) which should tell you how to compose functions with more than one variable. Mind you, I don't think it improves readability.
Dominic.
Interesting function. It got a sibling: (.)(.) :: (a1 -> b -> c) -> a1 -> (a -> b) -> a -> c Anybody knows how to intepret that? I tried to call it with (++) "t" (++"s") "it" but suddenly got distracted.

Martin Lütke wrote:
Dominic Steinitz schrieb:
Look at the type of (.).(.) which should tell you how to compose functions with more than one variable. Mind you, I don't think it improves readability.
Dominic.
Interesting function. It got a sibling: (.)(.) :: (a1 -> b -> c) -> a1 -> (a -> b) -> a -> c
Anybody knows how to intepret that? I tried to call it with (++) "t" (++"s") "it" but suddenly got distracted.
All I know is that most of this stuff looks like ASCII art - and perhaps I need to see a doctor...

Hello Andrew, Tuesday, September 25, 2007, 5:21:35 PM, you wrote:
Interesting function. It got a sibling: (.)(.) :: (a1 -> b -> c) -> a1 -> (a -> b) -> a -> c
sexy function with sexy type :) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Tue, 2007-09-25 at 11:40 +0100, Andrew Coppin wrote:
Aaron Denney wrote:
On 2007-09-25, Andrew Coppin
wrote: OK, *now* I'm puzzled... Why does map . map type-check?
(map . map) = (.) map map
(.) :: (a -> b) -> (b -> c) -> a -> c = (a -> b) -> (b -> c) -> (a -> c)
The first two arguments of (.) are 1-argument functions.
map :: (d -> e) -> [d] -> [e] = (d -> e) -> ([d] -> [e])
map is either a two argument function _or_ a function that takes one argument (a function) and returns a function.
In this latter view, for the first argument, of (.), we need:
a = d -> e b = [d] -> [e]
And for the second we know b = [d] -> [e] so c = [[d]] -> [[e]]
for everything to be consistent.
It's much clearer when you think of map not as "running this function over this list", but rather "turning this function that operates on elements into a function that operates on lists". Doing that twice (by composing) turns a function that operates on elements into a function that operates on lists of lists.
I just found it rather surprising. Every time *I* try to compose with functions of more than 1 argument, the type checker complains. Specifically, suppose you have
foo = f3 . f2 . f1
Assuming those are all 1-argument functions, it works great. But if f1 is a *two* argument function (like map is), the type checker refuses to allow it, and I have to rewrite it as
foo x y = f3 $ f2 $ f1 x y
which is really extremely annoying...
I'm just curiose as to why the type checker won't let *me* do it, but it will let *you* do it. (Maybe it hates me?)
In f . g, if g takes two arguments, f has to take a function as the first argument (because that's what g returns). jcc

It's reasonably easy to read.
But you could make it more readable. Type signatures, naming the first
lambda...
On 9/25/07, Andrew Coppin
While using Haskell, I often find myself writing convoluted constructions such as this:
show_system = unlines . zipWith (\l ms -> "Eq" ++ show l ++ ": " ++ (concat $ intersperse " + " $ zipWith (\n x -> x ++ " x" ++ show n) [1..] (init ms)) ++ " = " ++ last ms ) [1..] . map (map (take 8 . show))
And people complain that *Perl* is bad? This function is quite obviously absurd. I mean, it works, but can *you* figure out what it does without running it? The question is, can anybody think of a better way to write this function? (And more generally, functions like it.)
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Tue, 25 Sep 2007, Lennart Augustsson wrote:
It's reasonably easy to read. But you could make it more readable. Type signatures, naming the first lambda...
It might be reasonable to define something like mapMatrix that happens to be map . map, too. Along with at least a type synonym for matrices. Name domain constructs rather than expecting people to reconstruct them from their implementations, in other words. -- flippa@flippac.org The task of the academic is not to scale great intellectual mountains, but to flatten them.

On 2007-09-25, Philippa Cowderoy
On Tue, 25 Sep 2007, Lennart Augustsson wrote:
It's reasonably easy to read. But you could make it more readable. Type signatures, naming the first lambda...
It might be reasonable to define something like mapMatrix that happens to be map . map, too. Along with at least a type synonym for matrices.
Yes, that's a good idea. Because it lets you change from the often annoying list-of-lists implementation to something more reasonable for e.g. transpose, as recently mentioned.
Name domain constructs rather than expecting people to reconstruct them from their implementations, in other words.
Right. But a list-of-lists isn't a terribly specific domain construct. When it's used without further semantics, I think map . map is the best translation of intent. -- Aaron Denney -><-
participants (12)
-
Aaron Denney
-
Andrew Coppin
-
Brandon S. Allbery KF8NH
-
Bulat Ziganshin
-
Dominic Steinitz
-
Dougal Stanton
-
Jonathan Cast
-
Lennart Augustsson
-
Martin Lütke
-
Neil Mitchell
-
Philippa Cowderoy
-
Tristan Allwood