Re: [Haskell-cafe] (Un)termination of overloading resolution
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.
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?
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?
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.
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.
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
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
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
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
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
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
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
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
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
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
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
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
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
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