Re: [Haskell-cafe] (Un)termination of overloading resolution

2006-02-27 Thread Martin Sulzmann

I was talking about *static* termination. Hence, the conditions
in the paper and the new one I proposed are of course incomplete.
I think that's your worry, isn't it? There are reasonable 
type-level programs which are rejected but will terminate for certain
goals. 

I think what you'd like is that each instance specifies its own
termination condition which can then be checked dynamically.
Possible but I haven't thought much about it. The simplest and most
efficient strategy seems to stop after n number of steps.

Martin


Roman Leshchinskiy writes:
  On Mon, 27 Feb 2006, Martin Sulzmann wrote:
 In case we have an n-ary type function T
 (or (n+1)-ary type class constraint T)
 the conditions says
 for each

 type T t1 ... tn = t

 (or rule T t1 ... tn x == t)

 then rank(ti)  rank(t) for each i=1,..,n
   
I'm probably misunderstanding something but doesn't this imply that we
cannot declare any instances for
   
  class C a b | a - b, b - a
   
which do not break the bound variable condition? This would remove one
of the main advantages fundeps have over associated types.
   
  
   Sure you can. For example,
  
   class C a b | a-b, b-a
   instance C [a] [a]
  
  Ah, sorry, my question was very poorly worded. What I meant to say was 
  that there are no instances declarations for C which satisfy your rule 
  above and, hence, all instances of C (or of any other class with 
  bidirectional dependencies) must satisfy the other, more restrictive 
  conditions. Is that correct?
  
   In your example below, you are not only breaking the Bound Variable
   Condition, but you are also breaking the Coverage Condition.
  
  Yes, but I'm breaking the rule you suggested only once :-) It was only 
  intended as a cute example. My worry, however, is that there are many 
  useful type-level programs similar to my example which are guaranteed to 
  terminate but which nevertheless do not satisfy the rules in your paper or 
  the one you suggested here. I think ruling those out is unavoidable if you 
  want to specify termination rules which every instance must satisfy 
  individually. But why not specify rules for sets of instances instead? 
  This is, for instance, what some theorem provers do for recursive 
  functions and it allows them to handle a wide range of those without 
  giving up decidability.
  
  Roman
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] rounding errors with real numbers.

2006-02-27 Thread Henning Thielemann


On Sun, 26 Feb 2006, Matthias Fischmann wrote:


I think this is the well-known issue of using real numbers in decimal
representation on a machine that thinks binary, but I don't know what
to do with it, and some of you maybe do.

I want to shift+stretch a list of doubles into a given interval.
example:

| x1 = [2, 3, 4, 5, 10]
| y1 = normInterval x1 0 1
| = y1 = [0.0,0.125,0.25,0.375,1.0]

The function that does this looks something like this:

| normInterval :: [Double] - Double - Double - [Double]
| normInterval ps lower upper = map (\ x - (x - oldLower) * stretch + lower) ps


Is there --^
a cancellation problem?

Maybe you should use a kind of convex combination, that is

(x-oldLower)*a + (oldUpper-x)*b
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


[Haskell-cafe] -fno-monomorphism-restriction makes type-inference ambiguous?

2006-02-27 Thread Eike Scholz
Note: This Question has already been asked on the haskell mailing list.
  But haskell-cafe seems to be more adequate for for this question.

Hi,

i have encountered a problem and I don't quite understand 
the reason why I get the results I get. 
ghci seems to infer different types for the same expression.

Consider that I have disabled the monomorphism restriction 
in module AGC.lhs (which is attached).

and I have a toplevel definition of:

 mylength = synAttr listLength

loding the module in ghci (6.4) gives (beside some correct warnings):

$ Ok, modules loaded: Main.
$ *Main :type synAttr
$ synAttr :: (Data b) = ((?stack::[Dyn]) = b - a) - Attr a

$ *Main :type listLength
$ listLength :: (?stack::[Dyn]) = List - Float

$ *Main :type (synAttr listLength)
$ (synAttr listLength) :: Attr Float

$ *Main :type mylength
$ mylength :: (?stack::[Dyn]) = Dyn - Dyn - [Dyn] - Maybe Float

$ *Main let mylength = synAttr listLength
$ *Main :type mylength
$ mylength :: Dyn - Dyn - [Dyn] - Maybe Float

where

 type Attr a = Dyn - Dyn - [Dyn]- Maybe a

the problem I have is that inferred types for the toplevel declaration
mylength differ from the verbatim equal definition in the Let
experssion.

for the toplevel it infers:

  mylength :: (?stack::[Dyn]) = Dyn - Dyn - [Dyn] - Maybe Float

for the let-Binding
 
  mylength :: Dyn - Dyn - [Dyn] - Maybe Float

and this is what I expected.

Has anyone an Idea, why this happens?

best regards,

  Eike Scholz

PS: Beware of the comments in the attached file. This file is under
heavy development. I am dyslexic and don't correct the comments 
while continuously rewriting code and comments. I hope that the comments
are useful anyway. 
The (+) (~) (#) operators are broken at the moment and don't work the
way intended.






{-# OPTIONS_GHC -fglasgow-exts #-}
{-# OPTIONS_GHC -fno-monomorphism-restriction #-}

 import Data.Typeable 
 import qualified Data.Dynamic as D
 import Data.Generics
 import Data.Maybe
 import Debug.Trace

 strace s = trace (show s) s

--
-- Description:
-- Attribute Grammar Combinators 

trying to model an attribute grammer by an combinator dsl
by going through the example from
 http://www.haskell.org/tmrwiki/WhyAttributeGrammarsMatter
 by Wouter Swierstra for The Monad.Reader Issue Four 01-07-05

lets start with rewriting the test defintions:

DATA Root
  | Root  list : List

DATA List
  | Nil
  | Cons  hd : Float tl : List


 data Root = Root List
   deriving (Typeable,Data,Show)


 data List = Cons Float List  -- head and tail are in prelude
   | Nil
   deriving (Typeable,Data,Show)

now lets look how an attribute and a semantic:

ATTR List [ | | length : Float]

SEM List
  | Nillhs.length  = 0.0
  | Cons   lhs.length  = 1.0 + @tail.length

the length value is somehow accessed by 
  
  nodeName.AttrName 
 
we'll use (+) for (.) since (.) is allready assigned
for the same reason well use mylength

Lets simply define a type specific listLength

 type SynSem c v = (?stack :: [Dyn]) = c - v

 listLength :: (?stack :: [Dyn]) = List - Float
 listLength Nil = 0  -- the parent gets explained later
 listLength (Cons _ tl) = (1 + (tl+mylength) ) -- length is prelude

This is quite straight, but uses the not jet defined attribute mylength,
We can define it with:

 mylength = synAttr listLength

we can define the sum Attribute in the same way:

 listSum :: SynSem List Float
 listSum Nil = 0  -- the parent gets explained later
 listSum (Cons v tl) = (v + (tl+mysum) ) -- length is prelude

 mysum = synAttr listSum

well syntesised symantics seem to be simple.

so lets look at the inherited sematics:

ATTR List [ avg : Float | | ]

SEM Root
  | Root   list.avg= @list.sum / @list.length

SEM List
  | Cons   tail.avg= @lhs.avg

lets assume we have allready defined the synthesised semantics for sum.

lets look at the cons example: it tells us, 
that the average at the tail is the local average.

With the combinators it is translates to:


 root_list_Avg :: InhSem Root List Float
 root_list_Avg (Root _) l  = ((l+mysum) / (l+mylength))
 
 cons_tail_Avg :: InhSem List List Float
 cons_tail_Avg parent@(Cons _ _) l  = parent~avg

 avg = (inhAttr root_list_Avg) -- at this point we want to drop the
?+ (inhAttr cons_tail_Avg) -- monomorphism restriction
 
now a test type

 tl = (Cons 2 (Cons 8 (Cons 20 (Cons 10 Nil 


when we want to acces a Attribute outside the semantic functions
we have initialise the parent stack

 l = tl?mylength

 s = tl?mysum

 av = (Root tl)?avg


--
-- Implementation

 type InhSem p c v = (?stack :: [Dyn]) = p - c - v

 data None = None -- these are special placeohlders
   deriving (Typeable,Data,Show,Read,Eq)
 data Any  = Any
   deriving (Typeable,Data,Show,Read,Eq)


the Attr agruments, are the parent, this node, and a stack (list) containing
all parents.

 

[Haskell-cafe] Re: -fno-monomorphism-restriction makes type-inference ambiguous?

2006-02-27 Thread Ben Rudiak-Gould

Eike Scholz wrote:

mylength = synAttr listLength


$ *Main :type synAttr
$ synAttr :: (Data b) = ((?stack::[Dyn]) = b - a) - Attr a

$ *Main :type listLength
$ listLength :: (?stack::[Dyn]) = List - Float

$ *Main :type (synAttr listLength)
$ (synAttr listLength) :: Attr Float

$ *Main :type mylength
$ mylength :: (?stack::[Dyn]) = Dyn - Dyn - [Dyn] - Maybe Float

where


type Attr a = Dyn - Dyn - [Dyn]- Maybe a


This may be a bug. But note that both interpretations are legitimate. One 
way of applying synAttr to listLength is first to apply ?stack to 
listLength, obtaining listLength' :: List - Float and creating a 
(?stack::[Dyn]) constraint on the application node, then specializing 
listLength' to the type (?stack::a) = List - Float, then passing that to 
synAttr.


Again, I recommend that you not try to use implicit parameters.

-- Ben

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] rounding errors with real numbers.

2006-02-27 Thread Matthias Fischmann

On Mon, Feb 27, 2006 at 03:11:35PM +0100, Henning Thielemann wrote:
 To: Matthias Fischmann [EMAIL PROTECTED]
 cc: haskell-cafe@haskell.org
 From: Henning Thielemann [EMAIL PROTECTED]
 Date: Mon, 27 Feb 2006 15:11:35 +0100 (MET)
 Subject: Re: [Haskell-cafe] rounding errors with real numbers.
 
 
 On Sun, 26 Feb 2006, Matthias Fischmann wrote:
 
 I think this is the well-known issue of using real numbers in decimal
 representation on a machine that thinks binary, but I don't know what
 to do with it, and some of you maybe do.
 
 I want to shift+stretch a list of doubles into a given interval.
 example:
 
 | x1 = [2, 3, 4, 5, 10]
 | y1 = normInterval x1 0 1
 | = y1 = [0.0,0.125,0.25,0.375,1.0]
 
 The function that does this looks something like this:
 
 | normInterval :: [Double] - Double - Double - [Double]
 | normInterval ps lower upper = map (\ x - (x - oldLower) * stretch + 
 lower) ps
 
 Is there --^
 a cancellation problem?

what's a cancellation problem?

 Maybe you should use a kind of convex combination, that is
 
 (x-oldLower)*a + (oldUpper-x)*b

i don't quite understand this either.  is 'x' an old element in my
input list and your expression is the corresponding new element?  then
how does the resulting curve relate to the original one?  does this
keep the ratios between distances between elements in the list intact?
(this is the property that i am interested in.)

but it sounds intriguing.  perhaps i should play with this a little
and find out myself.

thanks,
matthias


signature.asc
Description: Digital signature
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] rounding errors with real numbers.

2006-02-27 Thread Henning Thielemann


On Mon, 27 Feb 2006, Matthias Fischmann wrote:


On Mon, Feb 27, 2006 at 03:11:35PM +0100, Henning Thielemann wrote:


Is there a cancellation problem?


what's a cancellation problem?


zu deutsch: Auslöschung

cancellation happens for instance here: 1 + 1e-50 - 1 == 0


Maybe you should use a kind of convex combination, that is

(x-oldLower)*a + (oldUpper-x)*b


i don't quite understand this either.


You have to adjust 'a' and 'b' in order to obtain 0 on x==oldLower and 1 
on x==oldUpper. The expression still depends linearly on x (plus an 
absolute term). Maybe it reduces the cancellations if the lower and the 
upper bound differ much in magnitude.___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] PrefixMap: code review request

2006-02-27 Thread Robert Dockins


On Feb 27, 2006, at 2:30 PM, David F.Place wrote:


Hi,

I'm a newish Haskell hacker with way too much experience hacking  
Lisp.At first, I found my Haskell code looking very lisp-y.   I  
think my code is becoming more idiomatic haskell.  I would be very  
grateful to anyone who would take a glance at the code below and  
point out any un-idiomatic usages that jump out.  It's a small  
module from a program which looks for palindromes in a list of  
words.  Thanks very much.


[snip]


partList :: Ord k = [([k],v)]-[k]-[(k,[([k],v)])]
partList pairs alphabet = reverse . fst $ foldl' f ([],pairs) alphabet
where f (result,pairs) l = (result',rest)
  where (part,rest) = span ((==l) . head . fst) pairs
result' = if null part
 then result
 else (l,part):result


I don't think I've ever seen nested wheres before ;-)  I'd probably  
avoid that; it's really hard to read.  If your function is  
sufficiently complicated that it needs its own where clause, you  
should probably just promote it to the top level.  If it is truly  
internal, you can avoid exporting it with the module export list.


[snip]

 searchMap :: Ord k = (v - vv) - [k] - PrefixMap k v - [vv]

Humm... double v seems like a pretty poor choice for a type  
variable name.


[snip]


Just a couple of general comments:

-- you don't seem to like horizontal whitespace much.  I know, I  
know, whitespace placement can be a highly personal thing, but I find  
most haskellers usually use a bit more horizontal whitespace,  
particularly in function signatures.  The arrow is almost always  
written with surrounding spaces.  I personally like space after  
commas in tuples and lists.  Several of your list comprehensions  
would also be easier to read with a bit of whitespace.  I also tend  
to like '=' signs lined up in a column for lets, pattern function  
definitions and wheres.


-- Nested tuple and lists types are pretty hard to read.  In your  
code [([k],v)] appears a lot.  Consider defining a type alias for it.




Rob Dockins

Speak softly and drive a Sherman tank.
Laugh hard; it's a long way to the bank.
  -- TMBG



___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


[Haskell-cafe] socket TChan problem

2006-02-27 Thread Stefan Aeschbacher
HiI try to write a program that reads from a socket and communicates the result over a TChan and writes it to stdout. Somehow I can't seem to get it right,the result is only printed when I send ETX on the socket.
Attached is a sample program that shows the behvaviour.Any hints on where my error is or how I could debug such a problem is appreciated.regardsStefan
import IO
import Control.Concurrent
import Control.Concurrent.STM
import Network
import System.Posix.Unistd

readIfReady False _ _ = return ()
readIfReady True h chan = do
	line - hGetLine h
	atomically (writeTChan chan (line))

readFromHandle h chan = do
	ready - hReady h
	readIfReady ready h chan
	usleep 1000
	readFromHandle h chan

writeFromChan rchan = do
	line - atomically (readTChan rchan)
	hPutStr stdout (line ++ \n)
	hFlush stdout
	writeFromChan rchan

acceptConnection sock = do
	(h, hostname, port) - accept sock
	hSetBuffering h NoBuffering

	wchan - atomically newTChan
	rchan - atomically (dupTChan wchan)
	forkIO (readFromHandle h wchan)
	forkIO (writeFromChan rchan)
	acceptConnection sock

run = do
	sock - listenOn (PortNumber (fromIntegral 1080))
	acceptConnection sock

main = do withSocketsDo $ run



___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] PrefixMap: code review request

2006-02-27 Thread David F. Place


On Feb 27, 2006, at 3:11 PM, Robert Dockins wrote:

-- Nested tuple and lists types are pretty hard to read.  In your  
code [([k],v)] appears a lot.  Consider defining a type alias for it.


Funny, of course as an inveterate lisp hacker, I am completely  
insensitive to nesting depth.  :-)  Your suggestion cleans up the  
code quite nicely.



David F. Place
mailto:[EMAIL PROTECTED]

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] PrefixMap: code review request

2006-02-27 Thread Brian Hulley

David F.Place wrote:
[snip]

partList :: Ord k = [([k],v)]-[k]-[(k,[([k],v)])]
partList pairs alphabet = reverse . fst $ foldl' f ([],pairs) alphabet
where f (result,pairs) l = (result',rest)
  where (part,rest) = span ((==l) . head . fst) pairs
result' = if null part
 then result
 else (l,part):result


If the above code is put into a non-literate file as:

partList :: Ord k = [([k],v)]-[k]-[(k,[([k],v)])]
partList pairs alphabet = reverse . fst $ foldl' f ([],pairs) alphabet
where f (result,pairs) l = (result',rest)
  where (part,rest) = span ((==l) . head . fst) pairs
result' = if null part
 then result
 else (l,part):result

there is a parse error (using ghc) at the line beginning with result'. This 
binding doesn't line up with anything. Also the second 'where' is 
dangerously close to the column started by the 'f' after the first 'where' 
(may not be noticeable in this email due to whatever font it is being 
displayed in but it's only indented by one space) which makes it a bit 
tricky to read.


I suggest:

partList :: Ord k = [([k],v)]-[k]-[(k,[([k],v)])]
partList pairs alphabet = reverse . fst $ foldl' f ([],pairs) alphabet
   where
  f (result,pairs) l = (result',rest)
 where
(part,rest) = span ((==l) . head . fst) pairs
result' = if null part
 then result
 else (l,part):result

or:

partList :: Ord k = [([k],v)]-[k]-[(k,[([k],v)])]
partList pairs alphabet = reverse . fst $ foldl' f ([],pairs) alphabet
   where
  f (result,pairs) l = (result',rest)
 where
(part,rest) = span ((==l) . head . fst) pairs
result' =
   if null part
  then result
  else (l,part):result

because always starting an 'if' construct on a new line ensures that you 
will never ever have any problems with it's layout (especially helpful for 
people used to C) when you use it in a 'do' block.


Also, both the above variants ensure that your code can be edited using 
variable width fonts, any tabbing regime you like (as long as leading 
whitespace only has tabs and never spaces), and will be immune to identifier 
renamings. The golden rule for 'safe' layout is always to start a layout 
block on the next line after 'do' 'where' 'of' 'let' and to try to resist 
the tempation to save a line by squashing the first binding onto the same 
line as the 'let' etc.


The second variant has the added advantage that the horizontal indentation 
is kept under control (eg in the above all indenting is 3 spaces further in) 
whereas when people write things like


if p == q then let a = 4
 b = if a ==4 then let q = 2
s = 56

after only 3 indents the code is already half way across the screen (not to 
mention the fact that the above layout is completely brittle and can be 
destroyed by a simple search-and-replace op to change 'q' to 'hello')


Of course all the above is just easy-to-talk-about technicalities of layout 
and you were really interested in getting feedback about idiomatic usage - 
hopefully someone else will give some feedback about that (I'm too lazy...) 
:-)


Regards, Brian.


___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] PrefixMap: code review request

2006-02-27 Thread David F. Place


On Feb 27, 2006, at 5:54 PM, Brian Hulley wrote:

there is a parse error (using ghc) at the line beginning with  
result'. This binding doesn't line up with anything. Also the  
second 'where' is dangerously close to the column started by the  
'f' after the first 'where' (may not be noticeable in this email  
due to whatever font it is being displayed in but it's only  
indented by one space) which makes it a bit tricky to read.


Whoops, that's noise in the transmission.  In my original file and my  
original email, it is indented correctly.  As for other indentation  
issues, I always use whatever emacs suggests.  Is that not a good  
strategy?



David F. Place
mailto:[EMAIL PROTECTED]

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] PrefixMap: code review request

2006-02-27 Thread Brian Hulley

David F. Place wrote:

On Feb 27, 2006, at 5:54 PM, Brian Hulley wrote:


there is a parse error (using ghc) at the line beginning with
result'. This binding doesn't line up with anything. Also the
second 'where' is dangerously close to the column started by the
'f' after the first 'where' (may not be noticeable in this email
due to whatever font it is being displayed in but it's only
indented by one space) which makes it a bit tricky to read.


Whoops, that's noise in the transmission.  In my original file and my
original email, it is indented correctly.  As for other indentation
issues, I always use whatever emacs suggests.  Is that not a good
strategy?


I always think it's a bit like income tax! Over the centuries the rules have 
got more and more complicated and instead of simplifying everything, 
computers have allowed all this mess to survive which ultimately makes life 
difficult for us poor humans because no-one knows any more what's going on 
(except a computer which has its own agenda for humanity...)


Whoever thought up the original Haskell layout rule assumed that people 
would be happy using a single fixed width font, tabs set to 8 spaces, and 
didn't care about the brittleness of the code (in the face of identifier 
renamings) it allowed one to write. A like-minded person has then created an 
emacs mode which supports this. It is probably also possible to create an 
emacs macro to safely rename identifiers by first parsing the code, doing 
the renaming in the AST, then pretty-printing the code back into the file.


However if you voluntarily use only a restricted subset of all possible 
layouts, you end up with non-brittle code that can be edited in any editor 
(eg someone else's editor who doesn't understand emacs:-) ) and where safely 
renaming identifiers is just a simple text-based search-and-replace 
operation.


Of course having said all this, many people have strong personal views about 
different ways of laying out code, whether to use tabs or not, etc etc.


I was just using your example as an excuse for some consciousness-raising 
about safe vs brittle layouts, but of course at the end of the day everyone 
has to decide for themselves. For example you might find that the aesthetics 
or readability of a 'brittle' layout, or the ease of editing using the 
particular emacs mode, outweighs the disadvantages of its being brittle.


As Dr Flox said on Star Trek Enterprise to T'Pol Infinite diversity in 
infinite combinations !!! :-)


Best regards, Brian. 


___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] (Un)termination of overloading resolution

2006-02-27 Thread Roman Leshchinskiy
On Mon, 2006-02-27 at 16:43 +0800, Martin Sulzmann wrote:
 I was talking about *static* termination. Hence, the conditions
 in the paper and the new one I proposed are of course incomplete.

Just to clarify: by static you mean verifiable at instance definition
time (i.e. under the open world assumption) whereas dynamic is when the
instance is used (i.e. in a closed world)? Note that both are static
from a programmer's point of view, but this terminology definitely makes
sense here.

 I think that's your worry, isn't it? There are reasonable 
 type-level programs which are rejected but will terminate for certain
 goals.

My worry are type-level programs which are rejected but will provably
terminate for *all* goals.

 I think what you'd like is that each instance specifies its own
 termination condition which can then be checked dynamically.

That depends on what you mean by specifying a termination condition.
Suppose we want to solve C t1 ... tn = t. A possible rule might be: if
while solving this we ever come up with the goal C u1 ... un = u, then
the number of constructors in u1 ... un must be strictly less than the
number of constructors in t1 ... tn. Something similar to this should
guarantee termination but would still allow structural recursion on
types. Note that this doesn't even have to be fully dynamic - it can be
checked once per module by using instance declarations as generators, I
think.

 Possible but I haven't thought much about it. The simplest and most
 efficient strategy seems to stop after n number of steps.

Yes, but I don't really like that. Any n will be completely arbitrary
and rule out perfectly good type-level programs for no good reason. For
what it's worth, this is essentially what C++ does and people don't like
it and seem to largely ignore the limit specified in the standard.

Roman



___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] haskell programming guidelines

2006-02-27 Thread Cale Gibbard
On 24/02/06, John Meacham [EMAIL PROTECTED] wrote:
 On Fri, Feb 24, 2006 at 12:39:27PM -0500, Cale Gibbard wrote:
  I look at the above as generating a proof obligation for me as the
  programmer that the lookup will never fail, or at least the ability to
  convince myself. :) If you want to handle errors, you should actually
  handle them, not let your users get Irrefutable pattern failed
  messages. Also, if someone else later comes along and wants to catch
  that error, they have to either do it in IO, which can be fiddly if
  the error occurs deep in the evaluation of some structure, or they
  refactor your code so that it returns the error explicitly. Sure,
  irrefutable pattern matches are useful, but they shouldn't be used if
  you expect they'll ever fail.

 Ah, perhaps I wasn't clear. I don't ever expect these to fail. The
 reason I prefer irrefutable pattern matches to handwritten 'error'
 messages (at first) is so many months later when I introduce a subtle
 heisenbug I don't get a

 error: This shouldn't happen
 or worse
 error: Prelude.undefined

 but rather a nice error pointing right to the line number.

 anything I ever expect to fail for any reason other than a bug I put in
 a failing Monad with a suitably user digestable error message. So, I was
 comparing them to handwritten 'error' messages for announcing
 programming bugs. not handwritten 'error' messages for users to see
 (which really should be using 'fail' in a monad anyway).

 John

Well, this is an issue. Perhaps a version of error which makes the
line/column number available to its parameter would help... something
along the lines of

type SourcePos = (Integer, Integer)
-- possibly a data/newtype with a nicer Show instance
errorPos :: (SourcePos - String) - a

This would give all the benefits normally acquired from the expansion
of the syntax sugar while allowing you to additionally add any extra
messages you'd like. Further, you'd not be required to work in, say
the identity monad, in order to get line number messages for failures
(though in GHC at least, irrefutable pattern match failures in lambdas
and let also get line numbered).

I'm actually really against the inclusion of fail in the Monad class,
so finding a reasonable replacement for any constructive uses it might
have had is important to me.

 - Cale
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] haskell programming guidelines

2006-02-27 Thread John Meacham
On Mon, Feb 27, 2006 at 10:57:17PM -0500, Cale Gibbard wrote:
 Well, this is an issue. Perhaps a version of error which makes the
 line/column number available to its parameter would help... something
 along the lines of
 
 type SourcePos = (Integer, Integer)
 -- possibly a data/newtype with a nicer Show instance
 errorPos :: (SourcePos - String) - a


Yes, this is what jhc's SRCLOC_ANNOTATE addreses, more or less.

 This would give all the benefits normally acquired from the expansion
 of the syntax sugar while allowing you to additionally add any extra
 messages you'd like. Further, you'd not be required to work in, say
 the identity monad, in order to get line number messages for failures
 (though in GHC at least, irrefutable pattern match failures in lambdas
 and let also get line numbered).

Well, the benefit of the Identity monad is so that the user of a routine
can choose to recover gracefully by using a different monad, you only
use the Identity monad when you are making a choice to bottom out on
errors. using 'error' directly is not an option in said cases because it
would take away the ability of the user of a routine to catch errors
properly. error should only be used for reporting bugs that should never
happen, not user visible failure.

The writer of a library shouldn't decide how (non-buggy) failure should
be handled, the user of it should.

 I'm actually really against the inclusion of fail in the Monad class,
 so finding a reasonable replacement for any constructive uses it might
 have had is important to me.

I know you keep saying this, We start with the exact same premises and
goals, yet somehow come to the exact opposite conclusion. I have not
quite figured out why.

However, a quick survey shows that _every single_ monad defined in the
standard and fptools libraries has an interesting non-error 'fail'
method other than Identity, whose sole purpose is to turn 'fail's into
errors.  Separating out a MonadError with 'fail' seems rather odd as
every monad will be an instance of it! (including Identity, since
turning fails into errors is its main purpose)

(the monads like 'Reader' and 'Writer' are actually just shorthand for
ReaderT a Identity, the inner monad determines the failure mode)

John

-- 
John Meacham - ⑆repetae.net⑆john⑈
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] PrefixMap: code review request

2006-02-27 Thread Lennart Augustsson

David F.Place wrote:

partList :: Ord k = [([k],v)]-[k]-[(k,[([k],v)])]
partList pairs alphabet = reverse . fst $ foldl' f ([],pairs) alphabet
where f (result,pairs) l = (result',rest)
  where (part,rest) = span ((==l) . head . fst) pairs
result' = if null part
 then result
 else (l,part):result



I would write something like:
...
 where f (result, pairs) l =
 case span ((==l) . head . fst) pairs of
 ([],   rest) - (  result, rest)
 (part, rest) - ((l, part):result, rest)

   -- Lennart
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] PrefixMap: code review request

2006-02-27 Thread Cale Gibbard
On 27/02/06, David F. Place [EMAIL PROTECTED] wrote:

 On Feb 27, 2006, at 5:54 PM, Brian Hulley wrote:

  there is a parse error (using ghc) at the line beginning with
  result'. This binding doesn't line up with anything. Also the
  second 'where' is dangerously close to the column started by the
  'f' after the first 'where' (may not be noticeable in this email
  due to whatever font it is being displayed in but it's only
  indented by one space) which makes it a bit tricky to read.

 Whoops, that's noise in the transmission.  In my original file and my
 original email, it is indented correctly.  As for other indentation
 issues, I always use whatever emacs suggests.  Is that not a good
 strategy?

I find that I often really dislike emacs' choices for indenting -- so
much so that I had to turn off smart indenting altogether to avoid
holding down the spacebar all the time (of course it remaps tab). The
simple indenter gives all the possible sane selections for lining
things up and lets you indent further if you want. I'd normally go
with the first of Brian's suggestions for laying out an if expression
(condition on one line, then and else aligned at a deeper depth right
after it), unless the condition was really long, in which case I might
switch to the second to get a little more space on that first line.

I don't personally worry about tab width, as long as things line up in
any given function definition. I usually use 4 spaces when not
thinking about it, but sometimes 3, or even 2, or far more, if it
makes things look better. Most editors are quite good at putting the
cursor at the same indent level on the following line, and this is all
the help you tend to need in this regard when editing a file.

(Random musing: It would be neat to have an editor which dropped
subtle vertical bars under the start of blocks, to make it easier to
visually apply the layout rule, and see when you haven't indented far
enough, and the block is broken.)

Always tell your editor to leave tabs out of your files. There are
very few Haskell programmers who actually leave tabs in their source,
and mixing spaces and tabs can be deadly. You can set this in emacs by
adding
(setq-default indent-tabs-mode nil)
to your .emacs if you haven't already done so. If you also use vim,
set expandtab
is the line you want. :)

 - Cale
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] haskell programming guidelines

2006-02-27 Thread Cale Gibbard
On 27/02/06, John Meacham [EMAIL PROTECTED] wrote:
 On Mon, Feb 27, 2006 at 10:57:17PM -0500, Cale Gibbard wrote:
  Well, this is an issue. Perhaps a version of error which makes the
  line/column number available to its parameter would help... something
  along the lines of
 
  type SourcePos = (Integer, Integer)
  -- possibly a data/newtype with a nicer Show instance
  errorPos :: (SourcePos - String) - a


 Yes, this is what jhc's SRCLOC_ANNOTATE addreses, more or less.

  This would give all the benefits normally acquired from the expansion
  of the syntax sugar while allowing you to additionally add any extra
  messages you'd like. Further, you'd not be required to work in, say
  the identity monad, in order to get line number messages for failures
  (though in GHC at least, irrefutable pattern match failures in lambdas
  and let also get line numbered).

 Well, the benefit of the Identity monad is so that the user of a routine
 can choose to recover gracefully by using a different monad, you only
 use the Identity monad when you are making a choice to bottom out on
 errors. using 'error' directly is not an option in said cases because it
 would take away the ability of the user of a routine to catch errors
 properly. error should only be used for reporting bugs that should never
 happen, not user visible failure.

I'd argue that it would be better for the user to simply catch the
value returned which indicates error explicitly, and throw the error
themselves. This indicates that they have put thought into the fact
that the function may fail.

 The writer of a library shouldn't decide how (non-buggy) failure should
 be handled, the user of it should.

Right, which is why minimal types for expressing the failure should be
used, and the user should convert from those types to whatever larger
environment they have in mind. If your function is simply partial, use
Maybe, if you want to report error strings, use Either String. These
types easily lift into any monad which support similar functionality.
It also gives the users of your library more information about the
exact way in which your functions may fail, just by looking at the
type signatures, and gets them thinking about handling that failure.
An arbitrary monad m doesn't indicate anything about the failure modes
present.


  I'm actually really against the inclusion of fail in the Monad class,
  so finding a reasonable replacement for any constructive uses it might
  have had is important to me.

 I know you keep saying this, We start with the exact same premises and
 goals, yet somehow come to the exact opposite conclusion. I have not
 quite figured out why.

 However, a quick survey shows that _every single_ monad defined in the
 standard and fptools libraries has an interesting non-error 'fail'
 method other than Identity, whose sole purpose is to turn 'fail's into
 errors.  Separating out a MonadError with 'fail' seems rather odd as
 every monad will be an instance of it! (including Identity, since
 turning fails into errors is its main purpose)

 (the monads like 'Reader' and 'Writer' are actually just shorthand for
 ReaderT a Identity, the inner monad determines the failure mode)

 John

Well, that means that Reader, Writer and State, and any monad based
upon them or their transformers does not have a meaningful fail. IO
also does not have an interesting fail. It also means that all custom
monads based on state transformers, say, don't have interesting fails.
This is a very large chunk of the monads which people use in everyday
code! The List monad and Maybe monad have nice fails, and that's why
they should be in MonadZero.

I disagree that Identity, Reader, Writer, or State should be an
instance of MonadError or MonadZero. They should simply not be used
for that purpose. I'd like a monad hierarchy where if there is an
instance of a class for a monad, then none of the methods of that
class are identically bottom. It seems disingenuous to me to say that
some type constructor implements certain functionality, and then
implement it in a way which crashes the program. If you need failure
in your monad, add it explicitly via a transformer, and if you use
failure, you should express that via a class. Types and classes should
be meaningful and informative about this sort of thing.

 - Cale
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] haskell programming guidelines

2006-02-27 Thread John Meacham
On Tue, Feb 28, 2006 at 01:09:03AM -0500, Cale Gibbard wrote:
  Well, the benefit of the Identity monad is so that the user of a routine
  can choose to recover gracefully by using a different monad, you only
  use the Identity monad when you are making a choice to bottom out on
  errors. using 'error' directly is not an option in said cases because it
  would take away the ability of the user of a routine to catch errors
  properly. error should only be used for reporting bugs that should never
  happen, not user visible failure.
 
 I'd argue that it would be better for the user to simply catch the
 value returned which indicates error explicitly, and throw the error
 themselves. This indicates that they have put thought into the fact
 that the function may fail.

so does using runIdentity, that is the point of it. You are saying I
want failure to bottom out, just like using it as a 'Maybe' means you
only care about whether it has a result or using it as a 'Either' means
you want the result string or using it as a WriterT Foo IO means you
want to possibly collect some results and have fail throw an IO
exception.

I consider it bad style to spend code on cases you never expect to
happen, if it takes too much work to write code that fails properly on
bugs, people arn't (and definitly should not have to) do the extra work,
they will just write code that fails poorly. Monadic failure is
absolutely great for writing robust, concise, code.

  be handled, the user of it should.
 
 Right, which is why minimal types for expressing the failure should be
 used, and the user should convert from those types to whatever larger
 environment they have in mind. If your function is simply partial, use
 Maybe, if you want to report error strings, use Either String. These
 types easily lift into any monad which support similar functionality.
 It also gives the users of your library more information about the
 exact way in which your functions may fail, just by looking at the
 type signatures, and gets them thinking about handling that failure.
 An arbitrary monad m doesn't indicate anything about the failure modes
 present.

ack! The user of a library is who should get to choose how to deal with
the error case, not the library writer.

I'd hate to give up such very common idioms as

-- collect error messages from all failing parsers 
[ err | Left err - map parse xs] 

-- look up a string transformed by a map in another map, failing if it
-- is not in said other map.
runIdentity $ Map.lookup (concatMap (`Map.lookup` map) xs) smap

but the real power is when you combine monadic failure with combinators
and monad transformers

-- imagine some complicated function
f x xs = runWriterT $ mapM (\x - foofunc x = tell) xs

the great thing about this is it is transparent to failure! so you can
build arbitrarily complicated transformers while still letting the user
of 'f' decide what to do with failure. this is a great feature, if
foofunc returned a data type, the writer of 'f' would be forced to deal
with failure, and might (likely will) do something silly like call
'error'. 

I really don't like it when things fail via 'error'. monadic failure
means they don't have to. not only can they let the user decide how
failure should be handled, but Monads provide exactly the compositional
tools needed to combine code in a such a way that preserves that
property.

imagine if Map.lookup returned Maybe Int, but writeInt returned (Either
String Foo).

now suddenly you couldn't do 
 Map.lookup x map = writeInt

By prematurely deciding on an algebraic type, you seriously limit the
usability of your code.

you say

If your function is simply partial, use Maybe, if you want to report
error strings, use Either String.

which is exactly precicely what monadic failure lets you do. use the
routine in the way that makes sense. but more importantly it lets you write
monadic combinators that preserve said property.


 Well, that means that Reader, Writer and State, and any monad based
 upon them or their transformers does not have a meaningful fail. IO
 also does not have an interesting fail. It also means that all custom
 monads based on state transformers, say, don't have interesting fails.
 This is a very large chunk of the monads which people use in everyday
 code! The List monad and Maybe monad have nice fails, and that's why
 they should be in MonadZero.

IO definitly has an interesting fail, it throws a catchable IO
exception. (note, this is not the same as imprecise exceptions)

Reader,Writer, and State are stacked on top of Identity, which has error
as fail on purpose. if you don't like that you have the freedom to
either stack the transformer version on to another monad. Or there are
various transformers that give you an interesting 'fail' if you want it.
When you use Identity, you are saying 'error' is what you want. 


but in any case, you just stated the power of monadic fail right there.

monads based on Reader, Writer, State won't have an