* [Caml-list] What if exn was not an open type? @ 2017-10-20 9:56 Malcolm Matalka 2017-10-20 10:55 ` David Allsopp 2017-10-21 21:28 ` Nathan Moreau 0 siblings, 2 replies; 44+ messages in thread From: Malcolm Matalka @ 2017-10-20 9:56 UTC (permalink / raw) To: caml-list I have a question in two parts: 1. Would this be a good idea? Why? (I'll describe why I think it is) 2. If it were a good idea, is it feasible to do? Full question: Despite exceptions being a part of the language, there is a trend in many libraries to try to avoid using them. While I cannot find it, I recall someone (Daniel maybe?) saying that the standard API advice is that exceptions should not cross API boundaries. The short reason for why people seem to want to avoid exceptions (which I agree with) is that they side step the type system for helping you understand if your code is correct and handles all situations the code might experience. Since the exn type is open, it means that one can add any exception they want so it's not even known what exceptions you might get ahead of time. Another aspect of exceptions, which might be more of my personal experience, is that exceptions tend to be pretty useless after the fact. For example, forgetting to handle a Not_found exception is an exercise in pain. Maybe I'm just bad at this, but many exceptions just aren't that useful. End_of_file is another one that, IMO, makes the program flow pretty awkward and if you have multiple files you're reading from at the same time quite ugly. I tend to use wrappers that give me an option based API. Maybe I just bad at solving these problems though and I'm the problem. The consequence of this is that even though I put a lot of effort in my code trying to avoid exceptions, I can never actually know that I have succeeded unless I'm very defensive and wrap all foreign calls in some exception handling code. There are APIs for this, but if I mess up then I'm in a bad spot. My proposal is that exceptions becomes a closed type and they reflect what Java calls "errors", which are things your program logic should generally not handle but can choose to if it wants to (I think we call these failures in Ocaml). The two specific exceptions I can think if that should exist are: Assertion_failure and Out of Memory. Another one that I think might be nice but is open for debate is a Not_implemented_failure, I use something like this often while building a system. I'm sure there are a few more that people can think of are meaningful, but the point is these represent pretty bad situations that the program logic shouldn't handle except in special situations. Thanks for reading, /Malcolm ^ permalink raw reply [flat|nested] 44+ messages in thread
* RE: [Caml-list] What if exn was not an open type? 2017-10-20 9:56 [Caml-list] What if exn was not an open type? Malcolm Matalka @ 2017-10-20 10:55 ` David Allsopp 2017-10-20 11:21 ` Ivan Gotovchits 2017-10-20 17:07 ` Malcolm Matalka 2017-10-21 21:28 ` Nathan Moreau 1 sibling, 2 replies; 44+ messages in thread From: David Allsopp @ 2017-10-20 10:55 UTC (permalink / raw) To: Malcolm Matalka, caml-list Malcolm Matalka wrote: > I have a question in two parts: > > 1. Would this be a good idea? Why? (I'll describe why I think it is) > > 2. If it were a good idea, is it feasible to do? > > Full question: > > Despite exceptions being a part of the language, there is a trend in > many libraries to try to avoid using them. While I cannot find it, I > recall someone (Daniel maybe?) saying that the standard API advice is > that exceptions should not cross API boundaries. > > The short reason for why people seem to want to avoid exceptions (which > I agree with) is that they side step the type system for helping you > understand if your code is correct and handles all situations the code > might experience. Since the exn type is open, it means that one can > add any exception they want so it's not even known what exceptions you > might get ahead of time. > > Another aspect of exceptions, which might be more of my personal > experience, is that exceptions tend to be pretty useless after the > fact. For example, forgetting to handle a Not_found exception is an > exercise in pain. Maybe I'm just bad at this, but many exceptions just > aren't that useful. End_of_file is another one that, IMO, makes the > program flow pretty awkward and if you have multiple files you're > reading from at the same time quite ugly. I tend to use wrappers that > give me an option based API. Maybe I just bad at solving these > problems though and I'm the problem. > > The consequence of this is that even though I put a lot of effort in my > code trying to avoid exceptions, I can never actually know that I have > succeeded unless I'm very defensive and wrap all foreign calls in some > exception handling code. There are APIs for this, but if I mess up > then I'm in a bad spot. > > My proposal is that exceptions becomes a closed type and they reflect > what Java calls "errors", which are things your program logic should > generally not handle but can choose to if it wants to (I think we call > these failures in Ocaml). The two specific exceptions I can think if > that should exist are: Assertion_failure and Out of Memory. Another > one that I think might be nice but is open for debate is a > Not_implemented_failure, I use something like this often while building > a system. I'm sure there are a few more that people can think of are > meaningful, but the point is these represent pretty bad situations that > the program logic shouldn't handle except in special situations. Without wishing to open old debating wounds too much, the argument of exceptions as errors tends to come down as to whether the thing signalled by an exception is truly exceptional. Not_found, for example, in some scenarios is as unexpected or impossible as Invalid_argument. Historically, they're (ab)used for performance reasons, but some of the overhead of that is being addressed in flambda. Note that for some arguable design mistakes - e.g. End_of_file, you can use exception matching to get around this, e.g. match input_line ch with | data -> ... | exception End_of_file -> ... which means that the old pattern let data = try Some (input_line ch) with End_of_file -> None is only needed if you need to compile with OCaml < 4.02 If you haven't come across it, https://caml.inria.fr/pub/old_caml_site/ocamlexc/ocamlexc.htm is an interesting piece of older research around dealing with handling exceptions. What your proposal does overlook slightly is the use of exceptions for actual flow control. See for example, an oldish post of Alain Frisch's at https://www.lexifi.com/blog/static-exceptions. However, uses of exceptions like this may at some point be subsumed by Algebraic Effects which are being worked on by various people, mostly with multicore OCaml in mind. There's lots of links to that in https://github.com/ocamllabs/ocaml-multicore/wiki as well as other literature elsewhere online. HTH, David ^ permalink raw reply [flat|nested] 44+ messages in thread
* RE: [Caml-list] What if exn was not an open type? 2017-10-20 10:55 ` David Allsopp @ 2017-10-20 11:21 ` Ivan Gotovchits 2017-10-20 11:38 ` Simon Cruanes 2017-10-20 17:07 ` Malcolm Matalka 1 sibling, 1 reply; 44+ messages in thread From: Ivan Gotovchits @ 2017-10-20 11:21 UTC (permalink / raw) To: David Allsopp; +Cc: caml-list, Malcolm Matalka [-- Attachment #1: Type: text/plain, Size: 4810 bytes --] It's also a question of efficiency, signaling an absence of data with an exception is usually more efficient, than signaling the presence of data by wrapping it in some data constructor, as the latter needs an allocation. Thus a function that raised an exception is more basic, than a function that returns an optional value, as the former can be translated into the latter, but not vice versa if you take an allocation into account. On Oct 20, 2017 6:55 AM, "David Allsopp" <dra-news@metastack.com> wrote: > Malcolm Matalka wrote: > > I have a question in two parts: > > > > 1. Would this be a good idea? Why? (I'll describe why I think it is) > > > > 2. If it were a good idea, is it feasible to do? > > > > Full question: > > > > Despite exceptions being a part of the language, there is a trend in > > many libraries to try to avoid using them. While I cannot find it, I > > recall someone (Daniel maybe?) saying that the standard API advice is > > that exceptions should not cross API boundaries. > > > > The short reason for why people seem to want to avoid exceptions (which > > I agree with) is that they side step the type system for helping you > > understand if your code is correct and handles all situations the code > > might experience. Since the exn type is open, it means that one can > > add any exception they want so it's not even known what exceptions you > > might get ahead of time. > > > > Another aspect of exceptions, which might be more of my personal > > experience, is that exceptions tend to be pretty useless after the > > fact. For example, forgetting to handle a Not_found exception is an > > exercise in pain. Maybe I'm just bad at this, but many exceptions just > > aren't that useful. End_of_file is another one that, IMO, makes the > > program flow pretty awkward and if you have multiple files you're > > reading from at the same time quite ugly. I tend to use wrappers that > > give me an option based API. Maybe I just bad at solving these > > problems though and I'm the problem. > > > > The consequence of this is that even though I put a lot of effort in my > > code trying to avoid exceptions, I can never actually know that I have > > succeeded unless I'm very defensive and wrap all foreign calls in some > > exception handling code. There are APIs for this, but if I mess up > > then I'm in a bad spot. > > > > My proposal is that exceptions becomes a closed type and they reflect > > what Java calls "errors", which are things your program logic should > > generally not handle but can choose to if it wants to (I think we call > > these failures in Ocaml). The two specific exceptions I can think if > > that should exist are: Assertion_failure and Out of Memory. Another > > one that I think might be nice but is open for debate is a > > Not_implemented_failure, I use something like this often while building > > a system. I'm sure there are a few more that people can think of are > > meaningful, but the point is these represent pretty bad situations that > > the program logic shouldn't handle except in special situations. > > Without wishing to open old debating wounds too much, the argument of > exceptions as errors tends to come down as to whether the thing signalled > by an exception is truly exceptional. Not_found, for example, in some > scenarios is as unexpected or impossible as Invalid_argument. Historically, > they're (ab)used for performance reasons, but some of the overhead of that > is being addressed in flambda. Note that for some arguable design mistakes > - e.g. End_of_file, you can use exception matching to get around this, e.g. > > match input_line ch with > | data -> ... > | exception End_of_file -> ... > > which means that the old pattern > > let data = try Some (input_line ch) with End_of_file -> None > > is only needed if you need to compile with OCaml < 4.02 > > If you haven't come across it, https://caml.inria.fr/pub/old_ > caml_site/ocamlexc/ocamlexc.htm is an interesting piece of older research > around dealing with handling exceptions. > > What your proposal does overlook slightly is the use of exceptions for > actual flow control. See for example, an oldish post of Alain Frisch's at > https://www.lexifi.com/blog/static-exceptions. However, uses of > exceptions like this may at some point be subsumed by Algebraic Effects > which are being worked on by various people, mostly with multicore OCaml in > mind. There's lots of links to that in https://github.com/ocamllabs/ > ocaml-multicore/wiki as well as other literature elsewhere online. > > HTH, > > > David > > -- > Caml-list mailing list. Subscription management and archives: > https://sympa.inria.fr/sympa/arc/caml-list > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs [-- Attachment #2: Type: text/html, Size: 6126 bytes --] ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-20 11:21 ` Ivan Gotovchits @ 2017-10-20 11:38 ` Simon Cruanes 2017-10-20 16:54 ` Malcolm Matalka 2017-10-24 13:30 ` Richard W.M. Jones 0 siblings, 2 replies; 44+ messages in thread From: Simon Cruanes @ 2017-10-20 11:38 UTC (permalink / raw) To: Ivan Gotovchits; +Cc: David Allsopp, caml-list, Malcolm Matalka [-- Attachment #1: Type: text/plain, Size: 482 bytes --] Exceptions are also very useful as control structures sometimes. For example: - exiting from a traversal (graph, tree, `Foo.iter` functions) early - being able to return a result from any point in a very large algorithm (e.g. returning "sat" or "unsat" in a SAT solver, where the main algorithm can span several hundreds or thousands of lines). -- Simon Cruanes http://weusepgp.info/ key 49AA62B6, fingerprint 949F EB87 8F06 59C6 D7D3 7D8D 4AC0 1D08 49AA 62B6 [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-20 11:38 ` Simon Cruanes @ 2017-10-20 16:54 ` Malcolm Matalka 2017-10-20 19:47 ` Simon Cruanes 2017-10-24 13:30 ` Richard W.M. Jones 1 sibling, 1 reply; 44+ messages in thread From: Malcolm Matalka @ 2017-10-20 16:54 UTC (permalink / raw) To: Simon Cruanes; +Cc: Ivan Gotovchits, David Allsopp, caml-list Simon Cruanes <simon.cruanes.2007@m4x.org> writes: > Exceptions are also very useful as control structures sometimes. For > example: > > - exiting from a traversal (graph, tree, `Foo.iter` functions) early > - being able to return a result from any point in a very large algorithm > (e.g. returning "sat" or "unsat" in a SAT solver, where the main > algorithm can span several hundreds or thousands of lines). Seems like monads address this as well. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-20 16:54 ` Malcolm Matalka @ 2017-10-20 19:47 ` Simon Cruanes 2017-10-21 21:15 ` Malcolm Matalka 0 siblings, 1 reply; 44+ messages in thread From: Simon Cruanes @ 2017-10-20 19:47 UTC (permalink / raw) To: Malcolm Matalka; +Cc: Ivan Gotovchits, David Allsopp, caml-list [-- Attachment #1: Type: text/plain, Size: 334 bytes --] > Seems like monads address this as well. Perhaps, but at some significant cost in both convenience (>>= everywhere) and performance (for pure algorithmics, probably not much for systems programming). -- Simon Cruanes http://weusepgp.info/ key 49AA62B6, fingerprint 949F EB87 8F06 59C6 D7D3 7D8D 4AC0 1D08 49AA 62B6 [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-20 19:47 ` Simon Cruanes @ 2017-10-21 21:15 ` Malcolm Matalka 0 siblings, 0 replies; 44+ messages in thread From: Malcolm Matalka @ 2017-10-21 21:15 UTC (permalink / raw) To: Simon Cruanes; +Cc: Ivan Gotovchits, David Allsopp, caml-list Simon Cruanes <simon.cruanes.2007@m4x.org> writes: >> Seems like monads address this as well. > > Perhaps, but at some significant cost in both convenience (>>= > everywhere) and performance (for pure algorithmics, probably not much > for systems programming). Maybe flambda will address the performance side of things. I'd rather have do-notation than exceptions if the (>>=) is annoying. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-20 11:38 ` Simon Cruanes 2017-10-20 16:54 ` Malcolm Matalka @ 2017-10-24 13:30 ` Richard W.M. Jones 2017-10-24 19:02 ` Petter A. Urkedal [not found] ` <CALa9pHQ-nhWf4T0U5gDiKTduPiEeXSZPQ=DY6N1YNbCXqRohPQ@mail.gmail.com> 1 sibling, 2 replies; 44+ messages in thread From: Richard W.M. Jones @ 2017-10-24 13:30 UTC (permalink / raw) To: Simon Cruanes; +Cc: Ivan Gotovchits, David Allsopp, caml-list, Malcolm Matalka On Fri, Oct 20, 2017 at 01:38:16PM +0200, Simon Cruanes wrote: > Exceptions are also very useful as control structures sometimes. For > example: > > - exiting from a traversal (graph, tree, `Foo.iter` functions) early > - being able to return a result from any point in a very large algorithm > (e.g. returning "sat" or "unsat" in a SAT solver, where the main > algorithm can span several hundreds or thousands of lines). Since I first used OCaml I have wished for a simple (and type safe) return statement. Rich. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-24 13:30 ` Richard W.M. Jones @ 2017-10-24 19:02 ` Petter A. Urkedal 2017-11-04 18:44 ` Richard W.M. Jones [not found] ` <CALa9pHQ-nhWf4T0U5gDiKTduPiEeXSZPQ=DY6N1YNbCXqRohPQ@mail.gmail.com> 1 sibling, 1 reply; 44+ messages in thread From: Petter A. Urkedal @ 2017-10-24 19:02 UTC (permalink / raw) To: caml-list On 24 October 2017 at 15:30, Richard W.M. Jones <rich@annexia.org> wrote: > Since I first used OCaml I have wished for a simple (and type safe) > return statement. It's possible to wrap a bit type (and exception) safety about exceptions used for return: val with_return : (('a -> 'b) -> 'a) -> 'a let with_return (type b) f = let exception Return of b in try f (fun y -> raise (Return y)) with Return y -> y E.g. with_return (fun return -> List.iter (function 0 -> () | i -> return i) [0; 0; 24; 0]; -1);; ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-24 19:02 ` Petter A. Urkedal @ 2017-11-04 18:44 ` Richard W.M. Jones 2017-11-04 18:48 ` SP ` (3 more replies) 0 siblings, 4 replies; 44+ messages in thread From: Richard W.M. Jones @ 2017-11-04 18:44 UTC (permalink / raw) To: Petter A. Urkedal; +Cc: caml-list On Tue, Oct 24, 2017 at 09:02:55PM +0200, Petter A. Urkedal wrote: > On 24 October 2017 at 15:30, Richard W.M. Jones <rich@annexia.org> wrote: > > Since I first used OCaml I have wished for a simple (and type safe) > > return statement. > > It's possible to wrap a bit type (and exception) safety about > exceptions used for return: > > val with_return : (('a -> 'b) -> 'a) -> 'a > > let with_return (type b) f = > let exception Return of b in > try f (fun y -> raise (Return y)) with Return y -> y > > E.g. > > with_return (fun return -> List.iter (function 0 -> () | i -> return > i) [0; 0; 24; 0]; -1);; As promised, I tried rewriting some code with this style. The good news is that it does look a lot more like the original C code. The bad news is that with_return as defined above doesn't really work like the C return statement, as in the small example below. The example is very contrived but it reflects a problem that I found in real code. The problem is that the return statement could be called from many contexts, all with different types. The compiler expects to unify all these types (as the same type 'b) which is not possible. It wasn't immediately clear to me if this was solvable. Rich. ---------------------------------------------------------------------- let with_return (type b) f = let exception Return of b in try f (fun y -> raise (Return y)) with Return y -> y let f () = with_return (fun return -> if false then return "error"; let a = match Some "abc" with | None -> return "another error" | Some a -> a in a ) -- Richard Jones ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-04 18:44 ` Richard W.M. Jones @ 2017-11-04 18:48 ` SP 2017-11-04 18:53 ` Richard W.M. Jones 2017-11-04 19:01 ` Max Mouratov ` (2 subsequent siblings) 3 siblings, 1 reply; 44+ messages in thread From: SP @ 2017-11-04 18:48 UTC (permalink / raw) To: caml-list On 04/11/2017 18:44, Richard W.M. Jones wrote: > As promised, I tried rewriting some code with this style. The > good news is that it does look a lot more like the original C code. > The bad news is that with_return as defined above doesn't really work > like the C return statement, as in the small example below. The > example is very contrived but it reflects a problem that I found in > real code. The good news, is that if you really want to write this in C style, just write it in C and bind to it from OCaml. > The problem is that the return statement could be called from many > contexts, all with different types. The compiler expects to unify all > these types (as the same type 'b) which is not possible. > > It wasn't immediately clear to me if this was solvable. I think you can define a generic return. -- SP ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-04 18:48 ` SP @ 2017-11-04 18:53 ` Richard W.M. Jones 2017-11-04 19:03 ` SP 0 siblings, 1 reply; 44+ messages in thread From: Richard W.M. Jones @ 2017-11-04 18:53 UTC (permalink / raw) To: SP; +Cc: caml-list On Sat, Nov 04, 2017 at 06:48:34PM +0000, SP wrote: > On 04/11/2017 18:44, Richard W.M. Jones wrote: > > As promised, I tried rewriting some code with this style. The > > good news is that it does look a lot more like the original C code. > > The bad news is that with_return as defined above doesn't really work > > like the C return statement, as in the small example below. The > > example is very contrived but it reflects a problem that I found in > > real code. > > The good news, is that if you really want to write this in C style, just > write it in C and bind to it from OCaml. I don't want to write it in C, as OCaml has considerable advantages. In fact, the code was converted from C to gain these advantages already. > > The problem is that the return statement could be called from many > > contexts, all with different types. The compiler expects to unify all > > these types (as the same type 'b) which is not possible. > > > > It wasn't immediately clear to me if this was solvable. > > I think you can define a generic return. I'm not familiar with this, what is the "generic return"? Rich. -- Richard Jones ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-04 18:53 ` Richard W.M. Jones @ 2017-11-04 19:03 ` SP 0 siblings, 0 replies; 44+ messages in thread From: SP @ 2017-11-04 19:03 UTC (permalink / raw) To: caml-list On 04/11/2017 18:53, Richard W.M. Jones wrote: > I don't want to write it in C, as OCaml has considerable advantages. > In fact, the code was converted from C to gain these advantages > already. Might be best to avoid that style then. > I'm not familiar with this, what is the "generic return"? One way is to make a module which has different signatures depending on the module parameter. Then you can have different instances, like String.c_return and Int.c_return. Also I think there are ways you can make a function polymorphic, less tedious, but someone else might be better covering those. -- SP ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-04 18:44 ` Richard W.M. Jones 2017-11-04 18:48 ` SP @ 2017-11-04 19:01 ` Max Mouratov 2017-11-04 19:16 ` octachron 2017-11-05 18:02 ` Petter A. Urkedal 3 siblings, 0 replies; 44+ messages in thread From: Max Mouratov @ 2017-11-04 19:01 UTC (permalink / raw) To: Richard W.M. Jones; +Cc: caml-list Saturday, November 4, 2017, 11:44:01 PM, Richard wrote: > The problem is that the return statement could be called from many > contexts, all with different types. The compiler expects to unify all > these types (as the same type 'b) which is not possible. See how it's done in Batteries: type 'a t = 'a -> exn let return label value = raise (label value) let label (type u) (f : u t -> u) : u = let module M = struct exception Return of u end in try f (fun x -> M.Return x) with M.Return u -> u ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-04 18:44 ` Richard W.M. Jones 2017-11-04 18:48 ` SP 2017-11-04 19:01 ` Max Mouratov @ 2017-11-04 19:16 ` octachron 2017-11-05 17:41 ` Richard W.M. Jones 2017-11-05 18:02 ` Petter A. Urkedal 3 siblings, 1 reply; 44+ messages in thread From: octachron @ 2017-11-04 19:16 UTC (permalink / raw) To: caml-list On 04/11/2017 19:44, Richard W.M. Jones wrote: > The problem is that the return statement could be called from many > contexts, all with different types. The compiler expects to unify all > these types (as the same type 'b) which is not possible. This issue can be solved by making the type of return more precise, capturing the fact that return always raises: type 'a return = { return: 'b. 'a -> 'b } [@@unboxed] let with_return (type b) f = let exception Return of b in try f {return = (fun y -> raise (Return y))} with Return y -> y;; It becomes then possible to write let f () = with_return (fun {return} -> if false then return "error"; let a = match Some "abc" with | None -> return "another error" | Some a -> a in a ) — octachron. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-04 19:16 ` octachron @ 2017-11-05 17:41 ` Richard W.M. Jones 2017-11-05 18:39 ` Yaron Minsky 0 siblings, 1 reply; 44+ messages in thread From: Richard W.M. Jones @ 2017-11-05 17:41 UTC (permalink / raw) To: octachron; +Cc: caml-list On Sat, Nov 04, 2017 at 08:16:17PM +0100, octachron wrote: > On 04/11/2017 19:44, Richard W.M. Jones wrote: > > The problem is that the return statement could be called from many > > contexts, all with different types. The compiler expects to unify all > > these types (as the same type 'b) which is not possible. > This issue can be solved by making the type of return more precise, > capturing the fact that return always raises: > > type 'a return = { return: 'b. 'a -> 'b } [@@unboxed] > let with_return (type b) f = > let exception Return of b in > try f {return = (fun y -> raise (Return y))} with Return y -> y;; > > It becomes then possible to write > > let f () = > with_return (fun {return} -> > if false then return "error"; > let a = > match Some "abc" with > | None -> return "another error" > | Some a -> a in > a > ) This one works great, thanks. Rich. -- Richard Jones ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-05 17:41 ` Richard W.M. Jones @ 2017-11-05 18:39 ` Yaron Minsky 2017-11-05 20:49 ` Gabriel Scherer 0 siblings, 1 reply; 44+ messages in thread From: Yaron Minsky @ 2017-11-05 18:39 UTC (permalink / raw) To: Richard W.M. Jones; +Cc: octachron, caml-list FWIW, this idiom is supported by Base. https://github.com/janestreet/base/blob/master/src/with_return.mli I notice we're not yet using the unboxed attribute, though, so we should fix that... y On Sun, Nov 5, 2017 at 12:41 PM, Richard W.M. Jones <rich@annexia.org> wrote: > On Sat, Nov 04, 2017 at 08:16:17PM +0100, octachron wrote: >> On 04/11/2017 19:44, Richard W.M. Jones wrote: >> > The problem is that the return statement could be called from many >> > contexts, all with different types. The compiler expects to unify all >> > these types (as the same type 'b) which is not possible. >> This issue can be solved by making the type of return more precise, >> capturing the fact that return always raises: >> >> type 'a return = { return: 'b. 'a -> 'b } [@@unboxed] >> let with_return (type b) f = >> let exception Return of b in >> try f {return = (fun y -> raise (Return y))} with Return y -> y;; >> >> It becomes then possible to write >> >> let f () = >> with_return (fun {return} -> >> if false then return "error"; >> let a = >> match Some "abc" with >> | None -> return "another error" >> | Some a -> a in >> a >> ) > > This one works great, thanks. > > Rich. > > -- > Richard Jones > > -- > Caml-list mailing list. Subscription management and archives: > https://sympa.inria.fr/sympa/arc/caml-list > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-05 18:39 ` Yaron Minsky @ 2017-11-05 20:49 ` Gabriel Scherer 2017-11-05 21:48 ` Yaron Minsky 2017-11-05 21:53 ` Petter A. Urkedal 0 siblings, 2 replies; 44+ messages in thread From: Gabriel Scherer @ 2017-11-05 20:49 UTC (permalink / raw) To: Yaron Minsky; +Cc: Richard W.M. Jones, octachron, caml-list, hugo heuzard We also have return-by-local-exceptions in Batteries (the BatReturn module), but reports from js_of_ocaml users (namely, Clément Pit-Claudel) is that this style gets translated to extremely slow Javascript code -- functions from Batteries using this style (string search functions in particular) were a performance bottleneck on the javascript backend. I fixed the issue by rewriting all uses of BatReturn in Batteries libraries themselves. Long-term, it would be better to have Javascript backends produce better code on this, but I think it is rather difficult -- it is easy to compile efficiently local exceptions that are raised in the same basic block, but with_return takes a callback so you have to both inline and specialize before you are within reach of the optimization. On Sun, Nov 5, 2017 at 7:39 PM, Yaron Minsky <yminsky@janestreet.com> wrote: > FWIW, this idiom is supported by Base. > > https://github.com/janestreet/base/blob/master/src/with_return.mli > > I notice we're not yet using the unboxed attribute, though, so we > should fix that... > > y > > On Sun, Nov 5, 2017 at 12:41 PM, Richard W.M. Jones <rich@annexia.org> wrote: >> On Sat, Nov 04, 2017 at 08:16:17PM +0100, octachron wrote: >>> On 04/11/2017 19:44, Richard W.M. Jones wrote: >>> > The problem is that the return statement could be called from many >>> > contexts, all with different types. The compiler expects to unify all >>> > these types (as the same type 'b) which is not possible. >>> This issue can be solved by making the type of return more precise, >>> capturing the fact that return always raises: >>> >>> type 'a return = { return: 'b. 'a -> 'b } [@@unboxed] >>> let with_return (type b) f = >>> let exception Return of b in >>> try f {return = (fun y -> raise (Return y))} with Return y -> y;; >>> >>> It becomes then possible to write >>> >>> let f () = >>> with_return (fun {return} -> >>> if false then return "error"; >>> let a = >>> match Some "abc" with >>> | None -> return "another error" >>> | Some a -> a in >>> a >>> ) >> >> This one works great, thanks. >> >> Rich. >> >> -- >> Richard Jones >> >> -- >> Caml-list mailing list. Subscription management and archives: >> https://sympa.inria.fr/sympa/arc/caml-list >> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners >> Bug reports: http://caml.inria.fr/bin/caml-bugs > > -- > Caml-list mailing list. Subscription management and archives: > https://sympa.inria.fr/sympa/arc/caml-list > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-05 20:49 ` Gabriel Scherer @ 2017-11-05 21:48 ` Yaron Minsky 2017-11-05 21:53 ` Petter A. Urkedal 1 sibling, 0 replies; 44+ messages in thread From: Yaron Minsky @ 2017-11-05 21:48 UTC (permalink / raw) To: Gabriel Scherer; +Cc: Richard W.M. Jones, octachron, caml-list, hugo heuzard Yeah, this is a general problem with exception-catching idioms. We rand into effectively the same issue with respect to the Base's Map and Set implementations, which are rather slow on JavaScript. We haven't rewritten those to a more efficient style yet, though. y On Sun, Nov 5, 2017 at 3:49 PM, Gabriel Scherer <gabriel.scherer@gmail.com> wrote: > We also have return-by-local-exceptions in Batteries (the BatReturn > module), but reports from js_of_ocaml users (namely, Clément > Pit-Claudel) is that this style gets translated to extremely slow > Javascript code -- functions from Batteries using this style (string > search functions in particular) were a performance bottleneck on the > javascript backend. I fixed the issue by rewriting all uses of > BatReturn in Batteries libraries themselves. > Long-term, it would be better to have Javascript backends produce > better code on this, but I think it is rather difficult -- it is easy > to compile efficiently local exceptions that are raised in the same > basic block, but with_return takes a callback so you have to both > inline and specialize before you are within reach of the optimization. > > On Sun, Nov 5, 2017 at 7:39 PM, Yaron Minsky <yminsky@janestreet.com> wrote: >> FWIW, this idiom is supported by Base. >> >> https://github.com/janestreet/base/blob/master/src/with_return.mli >> >> I notice we're not yet using the unboxed attribute, though, so we >> should fix that... >> >> y >> >> On Sun, Nov 5, 2017 at 12:41 PM, Richard W.M. Jones <rich@annexia.org> wrote: >>> On Sat, Nov 04, 2017 at 08:16:17PM +0100, octachron wrote: >>>> On 04/11/2017 19:44, Richard W.M. Jones wrote: >>>> > The problem is that the return statement could be called from many >>>> > contexts, all with different types. The compiler expects to unify all >>>> > these types (as the same type 'b) which is not possible. >>>> This issue can be solved by making the type of return more precise, >>>> capturing the fact that return always raises: >>>> >>>> type 'a return = { return: 'b. 'a -> 'b } [@@unboxed] >>>> let with_return (type b) f = >>>> let exception Return of b in >>>> try f {return = (fun y -> raise (Return y))} with Return y -> y;; >>>> >>>> It becomes then possible to write >>>> >>>> let f () = >>>> with_return (fun {return} -> >>>> if false then return "error"; >>>> let a = >>>> match Some "abc" with >>>> | None -> return "another error" >>>> | Some a -> a in >>>> a >>>> ) >>> >>> This one works great, thanks. >>> >>> Rich. >>> >>> -- >>> Richard Jones >>> >>> -- >>> Caml-list mailing list. Subscription management and archives: >>> https://sympa.inria.fr/sympa/arc/caml-list >>> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners >>> Bug reports: http://caml.inria.fr/bin/caml-bugs >> >> -- >> Caml-list mailing list. Subscription management and archives: >> https://sympa.inria.fr/sympa/arc/caml-list >> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners >> Bug reports: http://caml.inria.fr/bin/caml-bugs ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-05 20:49 ` Gabriel Scherer 2017-11-05 21:48 ` Yaron Minsky @ 2017-11-05 21:53 ` Petter A. Urkedal 1 sibling, 0 replies; 44+ messages in thread From: Petter A. Urkedal @ 2017-11-05 21:53 UTC (permalink / raw) To: Gabriel Scherer Cc: Yaron Minsky, Richard W.M. Jones, octachron, caml-list, hugo heuzard On 5 November 2017 at 21:49, Gabriel Scherer <gabriel.scherer@gmail.com> wrote: > We also have return-by-local-exceptions in Batteries (the BatReturn > module), but reports from js_of_ocaml users (namely, Clément > Pit-Claudel) is that this style gets translated to extremely slow > Javascript code -- functions from Batteries using this style (string > search functions in particular) were a performance bottleneck on the > javascript backend. I fixed the issue by rewriting all uses of > BatReturn in Batteries libraries themselves. > Long-term, it would be better to have Javascript backends produce > better code on this, but I think it is rather difficult -- it is easy > to compile efficiently local exceptions that are raised in the same > basic block, but with_return takes a callback so you have to both > inline and specialize before you are within reach of the optimization. Sounds like something a PPX rewriter could solve. It could also avoid the need to manually scope it, if we accept the module-level let as the scope. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-04 18:44 ` Richard W.M. Jones ` (2 preceding siblings ...) 2017-11-04 19:16 ` octachron @ 2017-11-05 18:02 ` Petter A. Urkedal 2017-11-05 18:24 ` Richard W.M. Jones 3 siblings, 1 reply; 44+ messages in thread From: Petter A. Urkedal @ 2017-11-05 18:02 UTC (permalink / raw) To: Richard W.M. Jones; +Cc: caml-list I vouch for octachron solution to the lack of polymorphism in the return function. I tried to reformat the inspect_fs_unix_fstab.ml more along my own coding style to get a feel for the issue: https://gist.github.com/paurkedal/80b89c8fabe041e62eccc596d51f382b What doesn't work with this style is falling though to the next block, though I'm not sure that's I good for maintainability anyway. This is, however, even further away from the C style. Petter ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-05 18:02 ` Petter A. Urkedal @ 2017-11-05 18:24 ` Richard W.M. Jones 2017-11-05 18:55 ` Petter A. Urkedal 0 siblings, 1 reply; 44+ messages in thread From: Richard W.M. Jones @ 2017-11-05 18:24 UTC (permalink / raw) To: Petter A. Urkedal; +Cc: caml-list On Sun, Nov 05, 2017 at 07:02:12PM +0100, Petter A. Urkedal wrote: > I vouch for octachron solution to the lack of polymorphism in the > return function. > > I tried to reformat the inspect_fs_unix_fstab.ml more along my own > coding style to get a feel for the issue: > > https://gist.github.com/paurkedal/80b89c8fabe041e62eccc596d51f382b > > What doesn't work with this style is falling though to the next block, > though I'm not sure that's I good for maintainability anyway. This > is, however, even further away from the C style. That appears to mainly play with indentation, which just means that our tools won't work well. FWIW here's the updated version I ended up with: https://gist.github.com/rwmjones/426821925fd1250096d7ddaac4103a12#file-gistfile1-txt-L57 This was formed by going back to the original C code: https://github.com/libguestfs/libguestfs/blob/5b74dd98a7926191cc085fc36be57f58bd6d80d8/lib/inspect-fs-unix.c#L1273 and rearranging the OCaml code to look more like it, adding return statements to achieve this. For those wondering why, the aim here is to translate the code from C to OCaml as closely as possible (at first) in order to minimize the inevitable bugs that will be added during the translation. The C code is very well tested over years. I have plans to rework the code completely later to make real use of OCaml, but first don't break things. Rich. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-05 18:24 ` Richard W.M. Jones @ 2017-11-05 18:55 ` Petter A. Urkedal 0 siblings, 0 replies; 44+ messages in thread From: Petter A. Urkedal @ 2017-11-05 18:55 UTC (permalink / raw) To: Richard W.M. Jones; +Cc: caml-list On 5 November 2017 at 19:24, Richard W.M. Jones <rich@annexia.org> wrote: > That appears to mainly play with indentation, which just means that > our tools won't work well. Indeed. I missed your motivation to keep this as close to the C code as possible, as you explain: > For those wondering why, the aim here is to translate the code from C > to OCaml as closely as possible (at first) in order to minimize the > inevitable bugs that will be added during the translation. The C code > is very well tested over years. I have plans to rework the code > completely later to make real use of OCaml, but first don't break > things. Petter ^ permalink raw reply [flat|nested] 44+ messages in thread
[parent not found: <CALa9pHQ-nhWf4T0U5gDiKTduPiEeXSZPQ=DY6N1YNbCXqRohPQ@mail.gmail.com>]
* Re: [Caml-list] What if exn was not an open type? [not found] ` <CALa9pHQ-nhWf4T0U5gDiKTduPiEeXSZPQ=DY6N1YNbCXqRohPQ@mail.gmail.com> @ 2017-10-25 8:35 ` Richard W.M. Jones 2017-10-25 9:12 ` Philippe Veber ` (3 more replies) 0 siblings, 4 replies; 44+ messages in thread From: Richard W.M. Jones @ 2017-10-25 8:35 UTC (permalink / raw) To: Petter A. Urkedal; +Cc: ptoscano, caml-list On Tue, Oct 24, 2017 at 09:00:05PM +0200, Petter A. Urkedal wrote: > On 24 October 2017 at 15:30, Richard W.M. Jones <rich@annexia.org> wrote: > > Since I first used OCaml I have wished for a simple (and type safe) > > return statement. > > It's possible to wrap a bit type (and exception) safety about > exceptions used for return: > > val with_return : (('a -> 'b) -> 'a) -> 'a > > let with_return (type b) f = > let exception Return of b in > try f (fun y -> raise (Return y)) with Return y -> y > > E.g. > > with_return (fun return -> List.iter (function 0 -> () | i -> return > i) [0; 0; 24; 0]; -1);; Thanks, that's interesting. -- As mine was a bit of a "Hit and run" comment, let me expand on why a return statement is useful for the kind of dull code that I write. I often have to write functions of the form: let f () = if some_problem then ( printf "sorry, can't do that\n"; (* return *) ) else if some_other_problem then ( printf "sorry, can't do that either\n"; (* return *) ) else ( match something with | None -> (* return *) | Some v -> (* finally we get to do some work! *) ... ) Real code is often heavily indented, or we have to put the work into awkward nested functions. This file contains a few real examples: https://github.com/libguestfs/libguestfs/blob/master/daemon/inspect_fs_unix_fstab.ml This would work a lot better with a return statement rather than workarounds. Having said all that I was writing a little ML language last year and I tried to implement a return statement, but it was very awkward to work out how to map that to my lambda calculus, so I understand how return statements are rather difficult to implement in practice. Rich. -- Richard Jones ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-25 8:35 ` Richard W.M. Jones @ 2017-10-25 9:12 ` Philippe Veber 2017-10-25 14:52 ` Richard W.M. Jones 2017-10-25 13:36 ` Ivan Gotovchits ` (2 subsequent siblings) 3 siblings, 1 reply; 44+ messages in thread From: Philippe Veber @ 2017-10-25 9:12 UTC (permalink / raw) To: Richard W.M. Jones; +Cc: Petter A. Urkedal, ptoscano, caml users [-- Attachment #1: Type: text/plain, Size: 1618 bytes --] 2017-10-25 10:35 GMT+02:00 Richard W.M. Jones <rich@annexia.org>: > On Tue, Oct 24, 2017 at 09:00:05PM +0200, Petter A. Urkedal wrote: > > On 24 October 2017 at 15:30, Richard W.M. Jones <rich@annexia.org> > wrote: > > > Since I first used OCaml I have wished for a simple (and type safe) > > > return statement. > > > > It's possible to wrap a bit type (and exception) safety about > > exceptions used for return: > > > > val with_return : (('a -> 'b) -> 'a) -> 'a > > > > let with_return (type b) f = > > let exception Return of b in > > try f (fun y -> raise (Return y)) with Return y -> y > > > > E.g. > > > > with_return (fun return -> List.iter (function 0 -> () | i -> return > > i) [0; 0; 24; 0]; -1);; > > Thanks, that's interesting. > > -- > > As mine was a bit of a "Hit and run" comment, let me expand on > why a return statement is useful for the kind of dull code that > I write. > > I often have to write functions of the form: > > let f () = > if some_problem then ( > printf "sorry, can't do that\n"; > (* return *) > ) > else if some_other_problem then ( > printf "sorry, can't do that either\n"; > (* return *) > ) > else ( > match something with > | None -> > (* return *) > | Some v -> > (* finally we get to do some work! *) > ... > ) > > Hi Richard, isn't that a context where error monads do a pretty decent job? check_some_problem () >>= fun () -> check_some_other_problem () >>= fun () -> expect_something () >>= fun something -> finally_do something Cheers, Philippe. [-- Attachment #2: Type: text/html, Size: 2561 bytes --] ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-25 9:12 ` Philippe Veber @ 2017-10-25 14:52 ` Richard W.M. Jones 2017-10-25 16:37 ` Ivan Gotovchits 2017-10-26 8:06 ` Malcolm Matalka 0 siblings, 2 replies; 44+ messages in thread From: Richard W.M. Jones @ 2017-10-25 14:52 UTC (permalink / raw) To: Philippe Veber; +Cc: Petter A. Urkedal, ptoscano, caml users On Wed, Oct 25, 2017 at 11:12:26AM +0200, Philippe Veber wrote: > isn't that a context where error monads do a pretty decent job? > > check_some_problem () >>= fun () -> > check_some_other_problem () >>= fun () -> > expect_something () >>= fun something -> > finally_do something Right, but the main problem with monads is they scare off ordinary programmers :-/ When writing open source code in OCaml we have two -- conflicting -- goals. Goal #1 is to write elegant, short, fast, safe code, and OCaml really wins there. Goal #2 is to attract outside programmers to work on the project, and that's pretty hard with OCaml code, but we manage it. But it gets much much harder if we use any concept which strays too far from imperative/C-like code. You will see if you look through our codebase that it's pretty imperative and -- quite deliberately -- avoids doing strange stuff with modules, functors or really anything which is "excessively functional" (sorry for the loose term, but I hope you know what I mean :-). However when I have the time after my current conference I will try to rewrite the code I linked to with monads to see if I can make something which is both simple and readable. Thanks, Rich. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-25 14:52 ` Richard W.M. Jones @ 2017-10-25 16:37 ` Ivan Gotovchits 2017-10-25 17:47 ` SP 2017-10-26 8:06 ` Malcolm Matalka 1 sibling, 1 reply; 44+ messages in thread From: Ivan Gotovchits @ 2017-10-25 16:37 UTC (permalink / raw) To: Richard W.M. Jones Cc: Philippe Veber, Petter A. Urkedal, ptoscano, caml users [-- Attachment #1: Type: text/plain, Size: 4053 bytes --] Well, we have the same constraint, as we are trying to write code, that is understandable by students, who may not know OCaml. However, we are also trying to enforce pure functional programming, that, we believe, teaches the right thinking and produces programs that are easier to understand. When you need to write system code or any code that deals with effects, monads become inevitable sooner or later unless you're willing to use the escape hatch of mutability. The monadic code usually scares people (and the Continuation monad usually scared even the bravest). However, there are ways to deal with it. You can use different syntax extensions, like the [ppx_let][1], that is very light, [ppx_monadic][2], that provides a real do-notation so that you can write your code in the true imperative style. You can even rely on camlp4 or camlp5 do provide you a full support for the do-notation. I, myself, do not really like the do-notation (even in Haskell, or F#) because I believe, that it hides the real notion of computation. So, we stick to the regular syntax of OCaml. I'm always explaining the concept of Monad using the imperative C-like language, by pointing that a monad is just a way to parametrize your semicolon operator. So the idea is not really from functional programming and should be understandable by someone who has only an imperative programming background. With all this said, I think, that your code should rely on exceptions, not the monads. Since libguestfs is totally imperative library, that deals with imperative primitives, using OCaml exceptions is a perfectly valid solution. You are already dealing with primitives that bear hidden effects, so your computations are not pure on the first hand, thus adding exceptions will not add anything more to it. The essence of each monad is the `run` function, that evaluates monadic operations (orders) and produces an explicit state. Since you can't really reify the run function in your case, using monads will only obscure things. To make the long story short, you should use monad only with pure code in cases when you can represent effects as an OCaml value. Mixing imperative code with monadic code is the worst thing one can imagine - as you will deal with wolves in lamb's skins, functions that pretend to be pure while in fact, they are inherently effectful. [1]: https://blog.janestreet.com/let-syntax-and-why-you-should-use-it/ [2]: https://bitbucket.org/camlspotter/ppx_monadic On Wed, Oct 25, 2017 at 10:52 AM, Richard W.M. Jones <rich@annexia.org> wrote: > On Wed, Oct 25, 2017 at 11:12:26AM +0200, Philippe Veber wrote: > > isn't that a context where error monads do a pretty decent job? > > > > check_some_problem () >>= fun () -> > > check_some_other_problem () >>= fun () -> > > expect_something () >>= fun something -> > > finally_do something > > Right, but the main problem with monads is they scare off ordinary > programmers :-/ > > When writing open source code in OCaml we have two -- conflicting -- > goals. Goal #1 is to write elegant, short, fast, safe code, and OCaml > really wins there. Goal #2 is to attract outside programmers to work > on the project, and that's pretty hard with OCaml code, but we manage > it. But it gets much much harder if we use any concept which strays > too far from imperative/C-like code. You will see if you look through > our codebase that it's pretty imperative and -- quite deliberately -- > avoids doing strange stuff with modules, functors or really anything > which is "excessively functional" (sorry for the loose term, but I > hope you know what I mean :-). > > However when I have the time after my current conference I will try > to rewrite the code I linked to with monads to see if I can make > something which is both simple and readable. > > Thanks, > > Rich. > > -- > Caml-list mailing list. Subscription management and archives: > https://sympa.inria.fr/sympa/arc/caml-list > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs > [-- Attachment #2: Type: text/html, Size: 5147 bytes --] ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-25 16:37 ` Ivan Gotovchits @ 2017-10-25 17:47 ` SP 0 siblings, 0 replies; 44+ messages in thread From: SP @ 2017-10-25 17:47 UTC (permalink / raw) To: caml-list On 25/10/2017 17:37, Ivan Gotovchits wrote: > With all this said, I think, that your code should rely on exceptions, not > the monads. Since libguestfs is totally imperative library, that deals with > imperative primitives, using OCaml exceptions is a perfectly valid > solution. But try, if you can, to not let these exceptions cross any of your APIs. -- SP ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-25 14:52 ` Richard W.M. Jones 2017-10-25 16:37 ` Ivan Gotovchits @ 2017-10-26 8:06 ` Malcolm Matalka 2017-10-26 8:11 ` Xavier Leroy 1 sibling, 1 reply; 44+ messages in thread From: Malcolm Matalka @ 2017-10-26 8:06 UTC (permalink / raw) To: Richard W.M. Jones Cc: Philippe Veber, Petter A. Urkedal, ptoscano, caml users "Richard W.M. Jones" <rich@annexia.org> writes: > On Wed, Oct 25, 2017 at 11:12:26AM +0200, Philippe Veber wrote: >> isn't that a context where error monads do a pretty decent job? >> >> check_some_problem () >>= fun () -> >> check_some_other_problem () >>= fun () -> >> expect_something () >>= fun something -> >> finally_do something > > Right, but the main problem with monads is they scare off ordinary > programmers :-/ F# seems to have limited support for monads, but they call them "workflows" and "computation statements" (or something like that). Maybe just a name change would be sufficient for Ocaml folks. We can tell everyone it's just monads still, but we want to call them blah. I dunno, how effective that is. > > When writing open source code in OCaml we have two -- conflicting -- > goals. Goal #1 is to write elegant, short, fast, safe code, and OCaml > really wins there. Goal #2 is to attract outside programmers to work > on the project, and that's pretty hard with OCaml code, but we manage > it. But it gets much much harder if we use any concept which strays > too far from imperative/C-like code. You will see if you look through > our codebase that it's pretty imperative and -- quite deliberately -- > avoids doing strange stuff with modules, functors or really anything > which is "excessively functional" (sorry for the loose term, but I > hope you know what I mean :-). > > However when I have the time after my current conference I will try > to rewrite the code I linked to with monads to see if I can make > something which is both simple and readable. > > Thanks, > > Rich. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-26 8:06 ` Malcolm Matalka @ 2017-10-26 8:11 ` Xavier Leroy 0 siblings, 0 replies; 44+ messages in thread From: Xavier Leroy @ 2017-10-26 8:11 UTC (permalink / raw) To: caml-list On 26/10/2017 10:06, Malcolm Matalka wrote: > F# seems to have limited support for monads, but they call them > "workflows" and "computation statements" (or something like that). > Maybe just a name change would be sufficient for Ocaml folks. We can > tell everyone it's just monads still, but we want to call them blah. I > dunno, how effective that is. Simon Peyton Jones suggested "warm fuzzy things" as a better name for monads. There is even an Urban Dictionary entry about this: https://www.urbandictionary.com/define.php?term=Warm%20Fuzzy%20Thing - Xavier Leroy ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-25 8:35 ` Richard W.M. Jones 2017-10-25 9:12 ` Philippe Veber @ 2017-10-25 13:36 ` Ivan Gotovchits 2017-10-26 7:31 ` Petter A. Urkedal 2017-10-27 13:58 ` Oleg 3 siblings, 0 replies; 44+ messages in thread From: Ivan Gotovchits @ 2017-10-25 13:36 UTC (permalink / raw) To: Richard W.M. Jones; +Cc: Petter A. Urkedal, ptoscano, caml-list [-- Attachment #1: Type: text/plain, Size: 5626 bytes --] On Wed, Oct 25, 2017 at 4:35 AM, Richard W.M. Jones <rich@annexia.org> wrote: > > Having said all that I was writing a little ML language last > year and I tried to implement a return statement, but it was very > awkward to work out how to map that to my lambda calculus, so > I understand how return statements are rather difficult to implement > in practice. > > Rich. > > The return statement is in fact a tamed exception, as it provides an exceptional, i.e., a non-local control flow. However, exceptions are not first class citizens in functional programming languages as they are not explicitly represented in typed lambda calculus, and are not really represented in the type system (in a sence that an expression with and without exception has the same type). Thus dealing with expressions that may raise an exception is hard, as well as dealing with any expression that has a side effect. OCaml multicore project is actually bringing more than the multicore support, they also reify the effect system into the language. Thus exceptions, as well as the return statements, can be soon easily implemented on top of the effect system and can be reasoned in a much more natural way. With all that said, safe and first class exceptions can be implemented in plain OCaml, or any other functional programming language that provides higher order polymorphism (type classes, functors, etc). The limited form of an exception can be implemented even without it. As it was already noticed, the Maybe monad, as well as the Error monad, or any other monad that reifies non-total computations can be used to implement a poor man return statement. The problem is that when one computation yields the null value, the rest of the computation chain is still evaluated. This is not a big issue if a chain is written manually (i.e., is a block of code, like in your example), but it could be serious if the chain is very long (i.e., processing a big list), or even worse infininte. If the problem with a list of computations can be solved on the monad implementation (as we do in the [Monads][2] library), the problem with the infinite computation can be easily addressed in a non-total monad. The answer is to use the continuation passing style (CPS). We can use delimited continuations (as in the upcoming multicore), or we can use plain non-delimited continuations. The rough idea, is that if you have function that needs to escape the computation prematurely, you can pass a continuation to that function, e.g., let do_stuff return = if world_is_bad then return None else if moon_phase_is_bad then return None else return (compute_some_stuff ()) You may notice, that we still need the else statement as our return function has type `'a -> 'a`, but for the non-local return we need something of type `'a -> 'b`, to implement this we need to switch to the continuation passing style, where each computation is a continuation, i.e., a function of type `('a -> 'b) -> 'b`. For example, this is how the List.fold function will look in the CPS: let fold xs ~init ~f = let rec loop xs k = match xs with | [] -> k | x :: xs -> fun k' -> k (fun a -> loop xs (f a x) k') in loop xs (fun k -> k init) Programming in this style directly is a little bit cubersome, but the good news, is that the continuation passing style can be encoded as a monad. The [Continuation monad][2] has only one extra operator, called call/cc, that calls a function and passes the current continuation (i.e., a first class return statement) to it, e.g., let do_stuff ~cc:return = if world_is_bad then return () >>= fun () -> if moon_phase_is_bad then return () >>= fun () -> compute_some_stuff () let main () = Cont.call do_stuff The interesting thing with the non-delimited continuation is that `cc` here is a first class value that can be stored in a container, resumed multiple times, etc. This gives us enough freedom to implement lots of weird variants of a control flow, e.g., return statement, exceptions, couroutines, concurrency, and, probably, even the [COMEFROM][4] instruction. Basically, a continuation is a first class program label. Is it good or bad, you can decide yourself. Maybe this is too much freedom, however, this is totally a part of the lambda calculus, and is not using any type system escape hatches, like exceptions, even given that the `return` function has type `a -> 'b t`. P.S. Although I took your examples for demonstration purposes of the Cont monad, I still believe that for your particular case a non-total monad would be much nicer and more natural. Using the Monads library Error monad, it can be expressed as: let do_stuff () = if world_is_bad then failf "the world is wrong" () >>= fun () -> if moon_phase_is_bad then failf "look at the Moon!" () >>= fun () -> compute_some_stuff () The Error monad implements the Fail interface, that provides mechanisms for diverging the computation with an exception (not real exception of course), as well as provides the catch operator. The Error monad has a much stricter type discipline, and limited control flow variants, that can be considered as boon in most cases. [1]: http://kcsrk.info/ocaml/multicore/2015/05/20/effects-multicore/ [2]: http://binaryanalysisplatform.github.io/bap/api/v1.3.0/Monads.Std.html [3]: http://binaryanalysisplatform.github.io/bap/api/v1.3.0/Monads.Std.Monad.Cont.html [4]: https://en.wikipedia.org/wiki/COMEFROM [5]: http://binaryanalysisplatform.github.io/bap/api/v1.3.0/Monads.Std.Monad.Fail.S.html [-- Attachment #2: Type: text/html, Size: 7240 bytes --] ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-25 8:35 ` Richard W.M. Jones 2017-10-25 9:12 ` Philippe Veber 2017-10-25 13:36 ` Ivan Gotovchits @ 2017-10-26 7:31 ` Petter A. Urkedal 2017-10-27 13:58 ` Oleg 3 siblings, 0 replies; 44+ messages in thread From: Petter A. Urkedal @ 2017-10-26 7:31 UTC (permalink / raw) To: Richard W.M. Jones; +Cc: ptoscano, caml users On 25 October 2017 at 10:35, Richard W.M. Jones <rich@annexia.org> wrote: > On Tue, Oct 24, 2017 at 09:00:05PM +0200, Petter A. Urkedal wrote: >> let with_return (type b) f = >> let exception Return of b in >> try f (fun y -> raise (Return y)) with Return y -> y > As mine was a bit of a "Hit and run" comment, let me expand on > why a return statement is useful for the kind of dull code that > I write. > > I often have to write functions of the form: > > let f () = > if some_problem then ( > printf "sorry, can't do that\n"; > (* return *) > ) > else if some_other_problem then ( > printf "sorry, can't do that either\n"; > (* return *) > ) > else ( > match something with > | None -> > (* return *) > | Some v -> > (* finally we get to do some work! *) > ... > ) Using the above trick, this might be written in more C-like style as: let f () = with_return @@ fun return -> if some_problem then ( printf "sorry, can't do that\n"; return () ); if some_other_problem then ( printf "sorry, can't do that either\n"; return () ); let v = match something with | None -> return () | Some v -> v in (* finally we get to do some work! *) ... However, I would have written it: let f () = if some_problem then printf "sorry, can't do that\n" else if some_other_problem then printf "sorry, can't do that either\n" else (match something with | None -> (* return *) | Some v -> (* finally we get to do some work! *) ...) That is, treating the if ... else more like let ... in. However any ; would chop of the bodies of the conditionals, so this style only works when semicolons are parenthesised or rewritten as let () = ... in. Especially, the latter must be used if one wants to skip indenting an else block, but insert a debug statement: let f () = if some_problem then printf "sorry, can't do that\n" else if some_other_problem then printf "sorry, can't do that either\n" else let () = printf "no problems\n" in (match something with | None -> (* return *) | Some v -> (* finally we get to do some work! *) ...) > Real code is often heavily indented, or we have to put the work into > awkward nested functions. This file contains a few real examples: > https://github.com/libguestfs/libguestfs/blob/master/daemon/inspect_fs_unix_fstab.ml > > This would work a lot better with a return statement rather than > workarounds. Given the change of formatting above, also changes the perception: The issue now is not that the return is missing, but that one cannot *not* return from a conditional. Petter ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-25 8:35 ` Richard W.M. Jones ` (2 preceding siblings ...) 2017-10-26 7:31 ` Petter A. Urkedal @ 2017-10-27 13:58 ` Oleg 2017-10-27 14:24 ` Philippe Veber 3 siblings, 1 reply; 44+ messages in thread From: Oleg @ 2017-10-27 13:58 UTC (permalink / raw) To: rich; +Cc: ptoscano, caml-list It is interesting that we have this discussion about, even advocacy for, monads at the time effects are coming to the front stage. The language Eff (http://eff-lang.org), which is essentially OCaml, states right upfront its advantages over monads. (Monads do not compose.) Daan Leijen talk past month about the web server implemented in Koka stressed the absence of monads. In Koka, if we need an effectful operation, we just do it. As the Multicore OCaml project has shown, effects can be very efficiently implemented. I fully agree with Ivan Gotovchits that recommends Rich Jones' code rely on exceptions rather than monads. Where I disagree is the contention that ``When you need to write system code or any code that deals with effects, monads become inevitable sooner or later unless you're willing to use the escape hatch of mutability.'' Monads are not inevitable! First of all, not all effects can be represented as monads (which was pointed long time ago by Wadler himself). My talk at the ML workshop last month http://okmij.org/ftp/tagless-final/nondet-effect.html described several other effects that aren't monadic and that commitment to monads precludes several useful implementations (e.g., code generation, which cannot be thought in monadic terms). Hence, there is real harm in trying to squeeze everything into a monad. Incidentally, alternative ideas of effects as interactions go back to 1970s. Richard W.M. Jones wrote: > Having said all that I was writing a little ML language last > year and I tried to implement a return statement, but it was very > awkward to work out how to map that to my lambda calculus, so > I understand how return statements are rather difficult to implement > in practice. Perhaps this gives a hint that lambda-calculus isn't the best model of computation -- as the Founding Father has recognized very early on. There is a reason he spent his life after ML working on process calculi. Indeed, it takes lots of hops to implement a simple return statement -- not to speak about real IO -- whereas it a process calculus, we just say !a. Done. Sending the result to another process (or to the context) is a built-in operation. There are no continuations to pass around or capture, no monads (which become totally unnecessary), no binding. Erlang has shown very well that this way of programming is realistic, and rather effective. lambda-calculus has its uses: it works spectacularly well for what it has been designed for: expressing definitions (and logical derivations). However, just because it is possible to express arithmetic in lambda-calculus does not mean that we should be stuck with Church-numerals. There are better ways to handle numbers -- and there are better ways to handle communication and control -- outside lambda-calculus. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-27 13:58 ` Oleg @ 2017-10-27 14:24 ` Philippe Veber 2017-10-27 14:49 ` Leo White 2017-11-01 7:16 ` Oleg 0 siblings, 2 replies; 44+ messages in thread From: Philippe Veber @ 2017-10-27 14:24 UTC (permalink / raw) To: Oleg, Richard W.M. Jones, ptoscano, caml users [-- Attachment #1: Type: text/plain, Size: 3882 bytes --] Thanks Oleg, this is very interesting! I thought one big selling argument of monads was also that the type of the functions shows which effect it performs. As I understand it, it is not the case for effects, at least not in existing implementations like multicore ocaml. Isn't this considered a concern, just like exception-raising functions are increasingly discouraged at the benefit of explicitly specifying partial functions with a result type? Also, would you know a reference that shows that effects compose indeed a lot more easily than monads? In my (very limited) experience, composing two monads sometimes require a little thinking and not so trivial implementation to be defined. 2017-10-27 15:58 GMT+02:00 Oleg <oleg@okmij.org>: > > It is interesting that we have this discussion about, even advocacy > for, monads at the time effects are coming to the front stage. The > language Eff (http://eff-lang.org), which is essentially OCaml, states > right upfront its advantages over monads. (Monads do not compose.) > Daan Leijen talk past month about the web server implemented in Koka > stressed the absence of monads. In Koka, if we need an effectful > operation, we just do it. As the Multicore OCaml project has shown, > effects can be very efficiently implemented. > > I fully agree with Ivan Gotovchits that recommends Rich Jones' code > rely on exceptions rather than monads. Where I disagree is the > contention that ``When you need to write system code or any code that > deals with effects, monads become inevitable sooner or later unless > you're willing to use the escape hatch of mutability.'' Monads are not > inevitable! > > First of all, not all effects can be represented as monads (which was > pointed long time ago by Wadler himself). My talk at the ML workshop > last month > http://okmij.org/ftp/tagless-final/nondet-effect.html > described several other effects that aren't monadic and that > commitment to monads precludes several useful implementations (e.g., > code generation, which cannot be thought in monadic terms). Hence, > there is real harm in trying to squeeze everything into a > monad. Incidentally, alternative ideas of effects as interactions go > back to 1970s. > > Richard W.M. Jones wrote: > > Having said all that I was writing a little ML language last > > year and I tried to implement a return statement, but it was very > > awkward to work out how to map that to my lambda calculus, so > > I understand how return statements are rather difficult to implement > > in practice. > > Perhaps this gives a hint that lambda-calculus isn't the best model of > computation -- as the Founding Father has recognized very early > on. There is a reason he spent his life after ML working on process > calculi. Indeed, it takes lots of hops to implement a simple return > statement -- not to speak about real IO -- whereas it a process > calculus, we just say !a. Done. Sending the result to another process > (or to the context) is a built-in operation. There are no > continuations to pass around or capture, no monads (which become > totally unnecessary), no binding. Erlang has shown very well that this > way of programming is realistic, and rather effective. > > lambda-calculus has its uses: it works spectacularly well for what it > has been designed for: expressing definitions (and logical > derivations). However, just because it is possible to express > arithmetic in lambda-calculus does not mean that we should be stuck > with Church-numerals. There are better ways to handle numbers -- and > there are better ways to handle communication and control -- outside > lambda-calculus. > > -- > Caml-list mailing list. Subscription management and archives: > https://sympa.inria.fr/sympa/arc/caml-list > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs > [-- Attachment #2: Type: text/html, Size: 4955 bytes --] ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-27 14:24 ` Philippe Veber @ 2017-10-27 14:49 ` Leo White 2017-11-01 7:16 ` Oleg 1 sibling, 0 replies; 44+ messages in thread From: Leo White @ 2017-10-27 14:49 UTC (permalink / raw) To: caml-list [-- Attachment #1: Type: text/plain, Size: 617 bytes --] > I thought one big selling argument of monads was also that the type of > the functions shows which effect it performs. As I understand it, it > is not the case for effects, at least not in existing implementations > like multicore ocaml. Whilst the current implementation in multicore OCaml does not track effects at the type level,there is a prototype version which does: https://github.com/lpw25/ocaml-typed-effects I spoke about it at HOPE last year: https://www.youtube.com/watch?v=ibpUJmlEWi4&list=PLnqUlCo055hVLWbmrXyxqYXcJJWSgU6Z5&index=1 http://www.lpw25.net/talks/hope2016.pdf Regards, Leo [-- Attachment #2: Type: text/html, Size: 971 bytes --] ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-27 14:24 ` Philippe Veber 2017-10-27 14:49 ` Leo White @ 2017-11-01 7:16 ` Oleg 2017-11-04 17:52 ` Philippe Veber 1 sibling, 1 reply; 44+ messages in thread From: Oleg @ 2017-11-01 7:16 UTC (permalink / raw) To: philippe.veber; +Cc: caml-list, rich, nick.palladinos > I thought one big selling argument of monads was also that the type of > the functions shows which effect it performs. As I understand it, it > is not the case for effects, at least not in existing implementations > like multicore ocaml. ... Also, would you know a reference that shows > that effects compose indeed a lot more easily than monads? How expressive are the types of effectful computations pretty much depends on a particular type system in use. Let me cite from the message that I received from Nick Palladinos (CCed) the other week, who implemented extensible effects in F#, a rather close relative of OCaml. You can write the code as below let example () = eff { do! put 1 let! x = get () let! y = ask () return x + y } (Here, `eff' is a tag of so-called computational expressions of F#, a very handy feature). The *inferred* type is as follows val example : unit -> Eff<'U,int> when 'U :> Reader<int> and 'U :> State<int> clearly stating that example is an effectful expression that uses at least Reader and State effects. The example also illustrates composability, your second question: put/get and ask are operations of two distinct effects (State and Reader, resp). You can combine them freely within the same program. (I hope Nick will write a paper explaining his implementation so that we can all learn from it.) To give more references (in addition to Leo's work), I should point to Koka https://koka-lang.github.io/koka/doc/kokaspec.html which is an OCaml-like language based on effects and effect-typing. The language is mature enough to write a web server in it (as Daan Leijen described in his talk at the ML Family workshop this year). Other references are the recent Effekt library in Scala http://b-studios.de/scala-effekt/ and extensible-effect library http://okmij.org/ftp/Haskell/extensible/ which, although being first proposed for Haskell has also been ported to Scala (and now F#). Purescript's extensible effects are also similar. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-11-01 7:16 ` Oleg @ 2017-11-04 17:52 ` Philippe Veber 0 siblings, 0 replies; 44+ messages in thread From: Philippe Veber @ 2017-11-04 17:52 UTC (permalink / raw) To: Oleg, Philippe Veber, caml users, Richard W.M. Jones, nick.palladinos [-- Attachment #1: Type: text/plain, Size: 2279 bytes --] Thanks a lot Leo and Oleg for all this very exciting material! 2017-11-01 8:16 GMT+01:00 Oleg <oleg@okmij.org>: > > > > I thought one big selling argument of monads was also that the type of > > the functions shows which effect it performs. As I understand it, it > > is not the case for effects, at least not in existing implementations > > like multicore ocaml. ... Also, would you know a reference that shows > > that effects compose indeed a lot more easily than monads? > > How expressive are the types of effectful computations pretty much > depends on a particular type system in use. Let me cite from the message > that I received from Nick Palladinos (CCed) the other week, who implemented > extensible effects in F#, a rather close relative of OCaml. You can > write the code as below > > let example () = > eff { > do! put 1 > let! x = get () > let! y = ask () > return x + y > } > > (Here, `eff' is a tag of so-called computational expressions of F#, a > very handy feature). > > The *inferred* type is as follows > val example : unit -> Eff<'U,int> when 'U :> Reader<int> and 'U :> > State<int> > > clearly stating that example is an effectful expression that uses at > least Reader and State effects. The example also illustrates > composability, your second question: put/get and ask are operations of two > distinct effects (State and Reader, resp). You can combine them freely > within the same program. > > (I hope Nick will write a paper explaining his implementation so that > we can all learn from it.) > > To give more references (in addition to Leo's work), I should point to Koka > https://koka-lang.github.io/koka/doc/kokaspec.html > > which is an OCaml-like language based on effects and > effect-typing. The language is mature enough to write a web server in > it (as Daan Leijen described in his talk at the ML Family workshop > this year). > > Other references are the recent Effekt library in Scala > http://b-studios.de/scala-effekt/ > and extensible-effect library > http://okmij.org/ftp/Haskell/extensible/ > which, although being first proposed for Haskell has also been ported > to Scala (and now F#). Purescript's extensible effects are also > similar. > > > [-- Attachment #2: Type: text/html, Size: 3176 bytes --] ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-20 10:55 ` David Allsopp 2017-10-20 11:21 ` Ivan Gotovchits @ 2017-10-20 17:07 ` Malcolm Matalka 1 sibling, 0 replies; 44+ messages in thread From: Malcolm Matalka @ 2017-10-20 17:07 UTC (permalink / raw) To: David Allsopp; +Cc: caml-list David Allsopp <dra-news@metastack.com> writes: > Malcolm Matalka wrote: >> I have a question in two parts: >> >> 1. Would this be a good idea? Why? (I'll describe why I think it is) >> >> 2. If it were a good idea, is it feasible to do? >> >> Full question: >> >> Despite exceptions being a part of the language, there is a trend in >> many libraries to try to avoid using them. While I cannot find it, I >> recall someone (Daniel maybe?) saying that the standard API advice is >> that exceptions should not cross API boundaries. >> >> The short reason for why people seem to want to avoid exceptions (which >> I agree with) is that they side step the type system for helping you >> understand if your code is correct and handles all situations the code >> might experience. Since the exn type is open, it means that one can >> add any exception they want so it's not even known what exceptions you >> might get ahead of time. >> >> Another aspect of exceptions, which might be more of my personal >> experience, is that exceptions tend to be pretty useless after the >> fact. For example, forgetting to handle a Not_found exception is an >> exercise in pain. Maybe I'm just bad at this, but many exceptions just >> aren't that useful. End_of_file is another one that, IMO, makes the >> program flow pretty awkward and if you have multiple files you're >> reading from at the same time quite ugly. I tend to use wrappers that >> give me an option based API. Maybe I just bad at solving these >> problems though and I'm the problem. >> >> The consequence of this is that even though I put a lot of effort in my >> code trying to avoid exceptions, I can never actually know that I have >> succeeded unless I'm very defensive and wrap all foreign calls in some >> exception handling code. There are APIs for this, but if I mess up >> then I'm in a bad spot. >> >> My proposal is that exceptions becomes a closed type and they reflect >> what Java calls "errors", which are things your program logic should >> generally not handle but can choose to if it wants to (I think we call >> these failures in Ocaml). The two specific exceptions I can think if >> that should exist are: Assertion_failure and Out of Memory. Another >> one that I think might be nice but is open for debate is a >> Not_implemented_failure, I use something like this often while building >> a system. I'm sure there are a few more that people can think of are >> meaningful, but the point is these represent pretty bad situations that >> the program logic shouldn't handle except in special situations. > > Without wishing to open old debating wounds too much, the argument of exceptions as errors tends to come down as to whether the thing > signalled by an exception is truly exceptional. Not_found, for example, in some scenarios is as unexpected or impossible as > Invalid_argument. Historically, they're (ab)used for performance reasons, but some of the overhead of that is being addressed in > flambda. Note that for some arguable design mistakes - e.g. End_of_file, you can use exception matching to get around this, e.g. > > match input_line ch with > | data -> ... > | exception End_of_file -> ... > > which means that the old pattern > > let data = try Some (input_line ch) with End_of_file -> None > > is only needed if you need to compile with OCaml < 4.02 > > If you haven't come across it, https://caml.inria.fr/pub/old_caml_site/ocamlexc/ocamlexc.htm is an interesting piece of older research around dealing with handling exceptions. > > What your proposal does overlook slightly is the use of exceptions for actual flow control. See for example, an oldish post of Alain > Frisch's at https://www.lexifi.com/blog/static-exceptions. However, uses of exceptions like this may at some point be subsumed by Algebraic > Effects which are being worked on by various people, mostly with multicore OCaml in mind. There's lots of links to that in > https://github.com/ocamllabs/ocaml-multicore/wiki as well as other literature elsewhere online. > > HTH, > > > David Thank you for the response and reading material. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-20 9:56 [Caml-list] What if exn was not an open type? Malcolm Matalka 2017-10-20 10:55 ` David Allsopp @ 2017-10-21 21:28 ` Nathan Moreau 2017-10-22 12:39 ` Malcolm Matalka 1 sibling, 1 reply; 44+ messages in thread From: Nathan Moreau @ 2017-10-21 21:28 UTC (permalink / raw) To: Malcolm Matalka; +Cc: caml users Exceptions being an open type allows you to classify failures. from Unix.ml: let rec waitpid_non_intr pid = try waitpid [] pid with Unix_error (EINTR, _, _) -> waitpid_non_intr pid With a closed type, there is not much you can do to ensure that you catch the right type of exception here (matching on strings? seems much worse). Note that, as there is no break keyword in the language, you tend to use exceptions for control flow to avoid rewriting your for loops to while loops. You can write some APIs without exposing exceptions, the same way that you can write an API without exposing a global hash-table. Exceptions are just a tool, that allow you to work around some limitations / performance issues / make your life easier. Removing them is impossible at this point for compatibility reasons, so all you can do is avoid using them if it makes you sad (the same way you avoid using some parts of the language when you program in c++ / ecmascript / whatever language du jour). Nathan On 20 October 2017 at 11:56, Malcolm Matalka <mmatalka@gmail.com> wrote: > I have a question in two parts: > > 1. Would this be a good idea? Why? (I'll describe why I think it is) > > 2. If it were a good idea, is it feasible to do? > > Full question: > > Despite exceptions being a part of the language, there is a trend in > many libraries to try to avoid using them. While I cannot find it, I > recall someone (Daniel maybe?) saying that the standard API advice is > that exceptions should not cross API boundaries. > > The short reason for why people seem to want to avoid exceptions (which > I agree with) is that they side step the type system for helping you > understand if your code is correct and handles all situations the code > might experience. Since the exn type is open, it means that one can add > any exception they want so it's not even known what exceptions you might > get ahead of time. > > Another aspect of exceptions, which might be more of my personal > experience, is that exceptions tend to be pretty useless after the > fact. For example, forgetting to handle a Not_found exception is an > exercise in pain. Maybe I'm just bad at this, but many exceptions just > aren't that useful. End_of_file is another one that, IMO, makes the > program flow pretty awkward and if you have multiple files you're > reading from at the same time quite ugly. I tend to use wrappers that > give me an option based API. Maybe I just bad at solving these problems > though and I'm the problem. > > The consequence of this is that even though I put a lot of effort in my > code trying to avoid exceptions, I can never actually know that I have > succeeded unless I'm very defensive and wrap all foreign calls in some > exception handling code. There are APIs for this, but if I mess up then > I'm in a bad spot. > > My proposal is that exceptions becomes a closed type and they reflect > what Java calls "errors", which are things your program logic should > generally not handle but can choose to if it wants to (I think we call > these failures in Ocaml). The two specific exceptions I can think if > that should exist are: Assertion_failure and Out of Memory. Another one > that I think might be nice but is open for debate is a > Not_implemented_failure, I use something like this often while building > a system. I'm sure there are a few more that people can think of are > meaningful, but the point is these represent pretty bad situations that > the program logic shouldn't handle except in special situations. > > Thanks for reading, > /Malcolm > > -- > Caml-list mailing list. Subscription management and archives: > https://sympa.inria.fr/sympa/arc/caml-list > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-21 21:28 ` Nathan Moreau @ 2017-10-22 12:39 ` Malcolm Matalka 2017-10-22 13:08 ` Nathan Moreau 2017-10-24 11:11 ` SP 0 siblings, 2 replies; 44+ messages in thread From: Malcolm Matalka @ 2017-10-22 12:39 UTC (permalink / raw) To: Nathan Moreau; +Cc: caml users Nathan Moreau <nathan.moreau@m4x.org> writes: > Exceptions being an open type allows you to classify failures. > > from Unix.ml: > > let rec waitpid_non_intr pid = > try waitpid [] pid > with Unix_error (EINTR, _, _) -> waitpid_non_intr pid > > With a closed type, there is not much you can do to ensure that you > catch the right type of exception here (matching on strings? seems > much worse). I'm one of those people that uses result everywhere + result monad where the error case is a polymorphic variant. This lets me get the value of an open type with power of an exhaustive pattern match check. No strings needed. > > Note that, as there is no break keyword in the language, you tend to > use exceptions for control flow to avoid rewriting your for loops to > while loops. This is exactly what monads and applicatives give you: a way to short circuit computation when it's no longer needed. > > You can write some APIs without exposing exceptions, the same way that > you can write an API without exposing a global hash-table. I'm not sure what you mean here, I have not had to expose exceptions in any APIs I've written. I don't think they are bad APIs. > > Exceptions are just a tool, that allow you to work around some > limitations / performance issues / make your life easier. Removing > them is impossible at this point for compatibility reasons, so all you > can do is avoid using them if it makes you sad (the same way you avoid > using some parts of the language when you program in c++ / ecmascript > / whatever language du jour). My point is that I cannot avoid exceptions without being very defensive in my code. A dependency can always start throwing an exception and I have no tool to help me find that automatically. > > Nathan > > > On 20 October 2017 at 11:56, Malcolm Matalka <mmatalka@gmail.com> wrote: >> I have a question in two parts: >> >> 1. Would this be a good idea? Why? (I'll describe why I think it is) >> >> 2. If it were a good idea, is it feasible to do? >> >> Full question: >> >> Despite exceptions being a part of the language, there is a trend in >> many libraries to try to avoid using them. While I cannot find it, I >> recall someone (Daniel maybe?) saying that the standard API advice is >> that exceptions should not cross API boundaries. >> >> The short reason for why people seem to want to avoid exceptions (which >> I agree with) is that they side step the type system for helping you >> understand if your code is correct and handles all situations the code >> might experience. Since the exn type is open, it means that one can add >> any exception they want so it's not even known what exceptions you might >> get ahead of time. >> >> Another aspect of exceptions, which might be more of my personal >> experience, is that exceptions tend to be pretty useless after the >> fact. For example, forgetting to handle a Not_found exception is an >> exercise in pain. Maybe I'm just bad at this, but many exceptions just >> aren't that useful. End_of_file is another one that, IMO, makes the >> program flow pretty awkward and if you have multiple files you're >> reading from at the same time quite ugly. I tend to use wrappers that >> give me an option based API. Maybe I just bad at solving these problems >> though and I'm the problem. >> >> The consequence of this is that even though I put a lot of effort in my >> code trying to avoid exceptions, I can never actually know that I have >> succeeded unless I'm very defensive and wrap all foreign calls in some >> exception handling code. There are APIs for this, but if I mess up then >> I'm in a bad spot. >> >> My proposal is that exceptions becomes a closed type and they reflect >> what Java calls "errors", which are things your program logic should >> generally not handle but can choose to if it wants to (I think we call >> these failures in Ocaml). The two specific exceptions I can think if >> that should exist are: Assertion_failure and Out of Memory. Another one >> that I think might be nice but is open for debate is a >> Not_implemented_failure, I use something like this often while building >> a system. I'm sure there are a few more that people can think of are >> meaningful, but the point is these represent pretty bad situations that >> the program logic shouldn't handle except in special situations. >> >> Thanks for reading, >> /Malcolm >> >> -- >> Caml-list mailing list. Subscription management and archives: >> https://sympa.inria.fr/sympa/arc/caml-list >> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners >> Bug reports: http://caml.inria.fr/bin/caml-bugs ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-22 12:39 ` Malcolm Matalka @ 2017-10-22 13:08 ` Nathan Moreau 2017-10-24 11:11 ` SP 1 sibling, 0 replies; 44+ messages in thread From: Nathan Moreau @ 2017-10-22 13:08 UTC (permalink / raw) To: Malcolm Matalka; +Cc: caml users > I'm one of those people that uses result everywhere + result monad where > the error case is a polymorphic variant. This lets me get the value of > an open type with power of an exhaustive pattern match check. No > strings needed. > My point is that I cannot avoid exceptions without being very defensive > in my code. A dependency can always start throwing an exception and I > have no tool to help me find that automatically. 1. Polymorphic variants have not always been in the language. 2. Of course you are free to design your APIs the way you want; however, when you accept a dependency, there is no work around reading the documentation to know if exceptions can be thrown. The kind of language that allow you to do that is not ocaml. >> Note that, as there is no break keyword in the language, you tend to >> use exceptions for control flow to avoid rewriting your for loops to >> while loops. > This is exactly what monads and applicatives give you: a way to short > circuit computation when it's no longer needed. Yes, just another tool, for other kind of situations. There are scenarios where exceptions are more appropriate than sophisticated type mechanisms. On 22 October 2017 at 14:39, Malcolm Matalka <mmatalka@gmail.com> wrote: > Nathan Moreau <nathan.moreau@m4x.org> writes: > >> Exceptions being an open type allows you to classify failures. >> >> from Unix.ml: >> >> let rec waitpid_non_intr pid = >> try waitpid [] pid >> with Unix_error (EINTR, _, _) -> waitpid_non_intr pid >> >> With a closed type, there is not much you can do to ensure that you >> catch the right type of exception here (matching on strings? seems >> much worse). > > I'm one of those people that uses result everywhere + result monad where > the error case is a polymorphic variant. This lets me get the value of > an open type with power of an exhaustive pattern match check. No > strings needed. > >> >> Note that, as there is no break keyword in the language, you tend to >> use exceptions for control flow to avoid rewriting your for loops to >> while loops. > > This is exactly what monads and applicatives give you: a way to short > circuit computation when it's no longer needed. > >> >> You can write some APIs without exposing exceptions, the same way that >> you can write an API without exposing a global hash-table. > > I'm not sure what you mean here, I have not had to expose exceptions in > any APIs I've written. I don't think they are bad APIs. > >> >> Exceptions are just a tool, that allow you to work around some >> limitations / performance issues / make your life easier. Removing >> them is impossible at this point for compatibility reasons, so all you >> can do is avoid using them if it makes you sad (the same way you avoid >> using some parts of the language when you program in c++ / ecmascript >> / whatever language du jour). > > My point is that I cannot avoid exceptions without being very defensive > in my code. A dependency can always start throwing an exception and I > have no tool to help me find that automatically. > >> >> Nathan >> >> >> On 20 October 2017 at 11:56, Malcolm Matalka <mmatalka@gmail.com> wrote: >>> I have a question in two parts: >>> >>> 1. Would this be a good idea? Why? (I'll describe why I think it is) >>> >>> 2. If it were a good idea, is it feasible to do? >>> >>> Full question: >>> >>> Despite exceptions being a part of the language, there is a trend in >>> many libraries to try to avoid using them. While I cannot find it, I >>> recall someone (Daniel maybe?) saying that the standard API advice is >>> that exceptions should not cross API boundaries. >>> >>> The short reason for why people seem to want to avoid exceptions (which >>> I agree with) is that they side step the type system for helping you >>> understand if your code is correct and handles all situations the code >>> might experience. Since the exn type is open, it means that one can add >>> any exception they want so it's not even known what exceptions you might >>> get ahead of time. >>> >>> Another aspect of exceptions, which might be more of my personal >>> experience, is that exceptions tend to be pretty useless after the >>> fact. For example, forgetting to handle a Not_found exception is an >>> exercise in pain. Maybe I'm just bad at this, but many exceptions just >>> aren't that useful. End_of_file is another one that, IMO, makes the >>> program flow pretty awkward and if you have multiple files you're >>> reading from at the same time quite ugly. I tend to use wrappers that >>> give me an option based API. Maybe I just bad at solving these problems >>> though and I'm the problem. >>> >>> The consequence of this is that even though I put a lot of effort in my >>> code trying to avoid exceptions, I can never actually know that I have >>> succeeded unless I'm very defensive and wrap all foreign calls in some >>> exception handling code. There are APIs for this, but if I mess up then >>> I'm in a bad spot. >>> >>> My proposal is that exceptions becomes a closed type and they reflect >>> what Java calls "errors", which are things your program logic should >>> generally not handle but can choose to if it wants to (I think we call >>> these failures in Ocaml). The two specific exceptions I can think if >>> that should exist are: Assertion_failure and Out of Memory. Another one >>> that I think might be nice but is open for debate is a >>> Not_implemented_failure, I use something like this often while building >>> a system. I'm sure there are a few more that people can think of are >>> meaningful, but the point is these represent pretty bad situations that >>> the program logic shouldn't handle except in special situations. >>> >>> Thanks for reading, >>> /Malcolm >>> >>> -- >>> Caml-list mailing list. Subscription management and archives: >>> https://sympa.inria.fr/sympa/arc/caml-list >>> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners >>> Bug reports: http://caml.inria.fr/bin/caml-bugs ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-22 12:39 ` Malcolm Matalka 2017-10-22 13:08 ` Nathan Moreau @ 2017-10-24 11:11 ` SP 2017-10-24 11:16 ` Gabriel Scherer 1 sibling, 1 reply; 44+ messages in thread From: SP @ 2017-10-24 11:11 UTC (permalink / raw) To: caml-list On 22/10/2017 13:39, Malcolm Matalka wrote: > I'm one of those people that uses result everywhere + result monad where > the error case is a polymorphic variant. This lets me get the value of > an open type with power of an exhaustive pattern match check. No > strings needed. Nice! -- SP ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-24 11:11 ` SP @ 2017-10-24 11:16 ` Gabriel Scherer 2017-10-25 11:30 ` Malcolm Matalka 0 siblings, 1 reply; 44+ messages in thread From: Gabriel Scherer @ 2017-10-24 11:16 UTC (permalink / raw) To: SP; +Cc: caml users [-- Attachment #1: Type: text/plain, Size: 1544 bytes --] Participants to this discussion may be interested in the article "Catch me if you can", by David Teller, Arnaud Spiwack and Till Varoquaux, 2008: https://hal.inria.fr/inria-00432575 This is the year 2008 and ML-style exceptions are everywhere. Most modern > languages, whether academic or industrial, feature some variant of this > mechanism. Languages such as Java even feature static coverage-checking for > such exceptions, something not available for ML languages, at least not > without resorting to external tools. In this document, we demonstrate a > design principle and a tiny library for managing errors in a functional > manner, with static coverage-checking, automatically-inferred, structurally > typed and hierarchical exceptional cases, with a reasonable run-time > penalty. Our work is based on OCaml and features monads, polymorphic > variants, compile-time code rewriting and trace elements of black magic. On Tue, Oct 24, 2017 at 1:11 PM, SP <sp@orbitalfox.com> wrote: > On 22/10/2017 13:39, Malcolm Matalka wrote: > > I'm one of those people that uses result everywhere + result monad where > > the error case is a polymorphic variant. This lets me get the value of > > an open type with power of an exhaustive pattern match check. No > > strings needed. > > Nice! > > -- > SP > > -- > Caml-list mailing list. Subscription management and archives: > https://sympa.inria.fr/sympa/arc/caml-list > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs > [-- Attachment #2: Type: text/html, Size: 2506 bytes --] ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] What if exn was not an open type? 2017-10-24 11:16 ` Gabriel Scherer @ 2017-10-25 11:30 ` Malcolm Matalka 0 siblings, 0 replies; 44+ messages in thread From: Malcolm Matalka @ 2017-10-25 11:30 UTC (permalink / raw) To: Gabriel Scherer; +Cc: SP, caml users Gabriel Scherer <gabriel.scherer@gmail.com> writes: > Participants to this discussion may be interested in the article "Catch me > if you can", by David Teller, Arnaud Spiwack and Till Varoquaux, 2008: > > https://hal.inria.fr/inria-00432575 Thank you for this, Gabriel, very interesting. > > This is the year 2008 and ML-style exceptions are everywhere. Most modern >> languages, whether academic or industrial, feature some variant of this >> mechanism. Languages such as Java even feature static coverage-checking for >> such exceptions, something not available for ML languages, at least not >> without resorting to external tools. In this document, we demonstrate a >> design principle and a tiny library for managing errors in a functional >> manner, with static coverage-checking, automatically-inferred, structurally >> typed and hierarchical exceptional cases, with a reasonable run-time >> penalty. Our work is based on OCaml and features monads, polymorphic >> variants, compile-time code rewriting and trace elements of black magic. > > > On Tue, Oct 24, 2017 at 1:11 PM, SP <sp@orbitalfox.com> wrote: > >> On 22/10/2017 13:39, Malcolm Matalka wrote: >> > I'm one of those people that uses result everywhere + result monad where >> > the error case is a polymorphic variant. This lets me get the value of >> > an open type with power of an exhaustive pattern match check. No >> > strings needed. >> >> Nice! >> >> -- >> SP >> >> -- >> Caml-list mailing list. Subscription management and archives: >> https://sympa.inria.fr/sympa/arc/caml-list >> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners >> Bug reports: http://caml.inria.fr/bin/caml-bugs >> ^ permalink raw reply [flat|nested] 44+ messages in thread
end of thread, other threads:[~2017-11-05 21:53 UTC | newest] Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2017-10-20 9:56 [Caml-list] What if exn was not an open type? Malcolm Matalka 2017-10-20 10:55 ` David Allsopp 2017-10-20 11:21 ` Ivan Gotovchits 2017-10-20 11:38 ` Simon Cruanes 2017-10-20 16:54 ` Malcolm Matalka 2017-10-20 19:47 ` Simon Cruanes 2017-10-21 21:15 ` Malcolm Matalka 2017-10-24 13:30 ` Richard W.M. Jones 2017-10-24 19:02 ` Petter A. Urkedal 2017-11-04 18:44 ` Richard W.M. Jones 2017-11-04 18:48 ` SP 2017-11-04 18:53 ` Richard W.M. Jones 2017-11-04 19:03 ` SP 2017-11-04 19:01 ` Max Mouratov 2017-11-04 19:16 ` octachron 2017-11-05 17:41 ` Richard W.M. Jones 2017-11-05 18:39 ` Yaron Minsky 2017-11-05 20:49 ` Gabriel Scherer 2017-11-05 21:48 ` Yaron Minsky 2017-11-05 21:53 ` Petter A. Urkedal 2017-11-05 18:02 ` Petter A. Urkedal 2017-11-05 18:24 ` Richard W.M. Jones 2017-11-05 18:55 ` Petter A. Urkedal [not found] ` <CALa9pHQ-nhWf4T0U5gDiKTduPiEeXSZPQ=DY6N1YNbCXqRohPQ@mail.gmail.com> 2017-10-25 8:35 ` Richard W.M. Jones 2017-10-25 9:12 ` Philippe Veber 2017-10-25 14:52 ` Richard W.M. Jones 2017-10-25 16:37 ` Ivan Gotovchits 2017-10-25 17:47 ` SP 2017-10-26 8:06 ` Malcolm Matalka 2017-10-26 8:11 ` Xavier Leroy 2017-10-25 13:36 ` Ivan Gotovchits 2017-10-26 7:31 ` Petter A. Urkedal 2017-10-27 13:58 ` Oleg 2017-10-27 14:24 ` Philippe Veber 2017-10-27 14:49 ` Leo White 2017-11-01 7:16 ` Oleg 2017-11-04 17:52 ` Philippe Veber 2017-10-20 17:07 ` Malcolm Matalka 2017-10-21 21:28 ` Nathan Moreau 2017-10-22 12:39 ` Malcolm Matalka 2017-10-22 13:08 ` Nathan Moreau 2017-10-24 11:11 ` SP 2017-10-24 11:16 ` Gabriel Scherer 2017-10-25 11:30 ` Malcolm Matalka
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox