From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@sympa.inria.fr Delivered-To: caml-list@sympa.inria.fr Received: from mail3-relais-sop.national.inria.fr (mail3-relais-sop.national.inria.fr [192.134.164.104]) by sympa.inria.fr (Postfix) with ESMTPS id 9B7527EE5B for ; Tue, 27 May 2014 10:42:38 +0200 (CEST) Received-SPF: None (mail3-smtp-sop.national.inria.fr: no sender authenticity information available from domain of bmillwood@janestreet.com) identity=pra; client-ip=38.105.200.115; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="bmillwood@janestreet.com"; x-sender="bmillwood@janestreet.com"; x-conformance=sidf_compatible Received-SPF: Pass (mail3-smtp-sop.national.inria.fr: domain of bmillwood@janestreet.com designates 38.105.200.115 as permitted sender) identity=mailfrom; client-ip=38.105.200.115; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="bmillwood@janestreet.com"; x-sender="bmillwood@janestreet.com"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: None (mail3-smtp-sop.national.inria.fr: no sender authenticity information available from domain of postmaster@mxout1.mail.janestreet.com) identity=helo; client-ip=38.105.200.115; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="bmillwood@janestreet.com"; x-sender="postmaster@mxout1.mail.janestreet.com"; x-conformance=sidf_compatible X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: ApEBAPNOhFMmachznGdsb2JhbABZhDGCaqpalFABgQceDgEBAQEBBhYJPIIlAQEBBBIRBBkBATcBDwsLDSoCAiEBEgEFARwGEyKIDAMRAwKjKWqKMHeEfwEFmUQNhhoRBow8ghYHgnWBS5gBgXaNNYQBGCmEaQ X-IPAS-Result: ApEBAPNOhFMmachznGdsb2JhbABZhDGCaqpalFABgQceDgEBAQEBBhYJPIIlAQEBBBIRBBkBATcBDwsLDSoCAiEBEgEFARwGEyKIDAMRAwKjKWqKMHeEfwEFmUQNhhoRBow8ghYHgnWBS5gBgXaNNYQBGCmEaQ X-IronPort-AV: E=Sophos;i="4.98,917,1392159600"; d="scan'208";a="64299315" Received: from mxout2.mail.janestreet.com (HELO mxout1.mail.janestreet.com) ([38.105.200.115]) by mail3-smtp-sop.national.inria.fr with ESMTP/TLS/DHE-RSA-AES256-SHA; 27 May 2014 10:42:37 +0200 Received: from tot-smtp.delacy.com ([172.27.22.15] helo=tot-smtp) by mxout1.mail.janestreet.com with esmtps (TLSv1.2:DHE-RSA-AES256-GCM-SHA384:256) (Exim 4.82) (envelope-from ) id 1WpCxv-0004ep-Us for caml-list@inria.fr; Tue, 27 May 2014 04:42:36 -0400 Received: from tot-dmz-mxgoog1.delacy.com ([172.27.224.14] helo=mxgoog2.janestreet.com) by tot-smtp with esmtps (TLSv1:AES256-SHA:256) (Exim 4.72) (envelope-from ) id 1WpCxv-00079d-QB for caml-list@inria.fr; Tue, 27 May 2014 04:42:35 -0400 Received: from mail-qg0-f43.google.com ([209.85.192.43]) by mxgoog2.janestreet.com with esmtp (Exim 4.76) (envelope-from ) id 1WpCxv-0008KO-OL for caml-list@inria.fr; Tue, 27 May 2014 04:42:35 -0400 Received: by mail-qg0-f43.google.com with SMTP id 63so13412820qgz.16 for ; Tue, 27 May 2014 01:42:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=janestreet.com; s=google; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=GyJHgvv5aJ7AW1Q+5gmYC0rn3ZDasIeZRZSHfQt/YEU=; b=iqxsNZ8z1+itZyCzH7mcqRXHR5zYLxjLFcwOnxgGdx+6Ep/gZYhbtGpIX4q3r5aY5g wVk3Wvwmk8nExodGRFd43PCYVSqkzuwNsF/lCXlEx7GDNGuN9PjSyS0lHQrtXD/L4JIM 8w5i4wNHI14KhmrAVhRweQ+rV0vVWirHibkMk= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:date :message-id:subject:from:to:cc:content-type; bh=GyJHgvv5aJ7AW1Q+5gmYC0rn3ZDasIeZRZSHfQt/YEU=; b=UF4wUatYq8554xUTSbLhPLXPRytSE0+kYAdkUi3FEFc2ID7e7oZ8cqfaz1x/pTU3y8 FQWfIOPdhDbOY3GJk014IGADpr/M2Xzw2JYCRG1tthGpt36tmkM90TQmoz0hneD4014/ xcv/EM+m02L1xiNtxatJCXF1+L4X/6VJXhp+qMiIQM4EiglIIoNlyAFLhsdii7wb6oef XdiwocZoOxMiX2fmQ0cI2mPf7BwKXIG0w/kYn9P8e8/F+7mp418ubkFnKuEufBKTMWoR nt4WWaElPdh1RTsmE/GLc1bwJZL4OT8ASpQlSOM7aiPjXyUWdmXLdjCZmDMrvdfOIQL0 xtVQ== X-Gm-Message-State: ALoCoQmRuuBiUElrgemaZrJGbylo9zeuWOPnSNBbQ3fMjxf9f7r7W2eAEiY5JRGOcduxEefVG67kvWdjB9plUv325cTOUmJ3Daotk+Hdi6Bt8CAZwxjJGsBOpp0C0U/kVXbcyrdakY6Z X-Received: by 10.224.166.73 with SMTP id l9mr39714854qay.34.1401180155607; Tue, 27 May 2014 01:42:35 -0700 (PDT) MIME-Version: 1.0 X-Received: by 10.224.166.73 with SMTP id l9mr39714841qay.34.1401180155500; Tue, 27 May 2014 01:42:35 -0700 (PDT) Received: by 10.140.103.67 with HTTP; Tue, 27 May 2014 01:42:35 -0700 (PDT) In-Reply-To: References: <53835610.9050609@inria.fr> <53B801AD6F5B4BFBA0DA2A69D8775497@erratique.ch> Date: Tue, 27 May 2014 09:42:35 +0100 Message-ID: From: Ben Millwood To: Philippe Veber Cc: =?UTF-8?Q?Daniel_B=C3=BCnzli?= , Romain Bardou , caml users Content-Type: multipart/alternative; boundary=001a11c27e386f8c2904fa5dad16 Subject: Re: [Caml-list] Uncaught exceptions in function type. --001a11c27e386f8c2904fa5dad16 Content-Type: text/plain; charset=UTF-8 On 26 May 2014 17:02, Philippe Veber wrote: > > Using options or Result from time to time is certainly a nice habit. > However to be honest, using option/result monads extensively does not seem > right to me, like pulling the language in a direction it was not really > designed for. Monad combinators do a great job at propagating errors, but > still there is a serious addition of plumbing in function body's and Result > returning functions have a significantly less readable type. But I guess > this is pretty much a question of taste/habit to see if the added verbosity > is worth it. > It's true that using a monadic style does introduce *some* noise, but I don't think it's so bad, especially if you design your API to fit with the style. In a way I almost prefer having the binds there so that I can easily see where the possible interruption points in the flow are, and in particular it's /syntactically/ obvious where errors /can't/ occur. BTW core still uses exceptions. Is there an explicit rule as to how to > decide between Result type or exceptions. For instance, why not write the > Array.create function like this: > > val create : int -> 'a -> 'a array Or_error.t > > where create fails for a negative integer? > Daniel's summary is very good, although we do use Failure in Core, often for assertion-failure like scenarios or just any case where the error isn't meant to be handled specifically. In many cases Invalid_argument would arguably be more appropriate, but in other cases that isn't obvious (say if you mutate a Queue.t while you are folding over it, that will be detected and an error will be thrown, but it isn't /really/ an invalid argument error), and in any case since we often don't intend to catch them except to print a stack trace and die, it doesn't matter too much. We also do just break our own rules sometimes. Possible reasons why are: - some aspect of usability takes priority over dogmatics (although this is much rarer than you'd think) - legacy exceptions (see below) - sometimes we make bad decisions :( Having no experience on large projects, it is not clear to me why > exceptions are considered so dangerous. One point I see is that when you > decide to handle errors with a try ... with expression, there is no > exhaustivity check to ensure you handled all possible cases, and that you > have to rely on (possibly incomplete) documentation and reasonning to > convince yourself you forgot nothing. Another related one is that there is > no automatic analysis to show exceptions thrown by a function in the > documentation. These are of course already strong reasons why not to use > too many exceptions (and were the motivation for my initial question). Do > you see other reasons? > One particularly galling aspect of exceptions not showing up in types is that they can't easily be refactored. If you decide to change which exceptions a given function throws, you can't rely on the typechecker to flag up where code was written to the old specification and needs changing. You can't even just eyeball the code to see where the function was used and see if the exception was caught, because who knows where in the call stack it might have been handled? Explicit error types, on the other hand, will refuse to compile unless you have changed your code to deal with it, so you can make your errors richer or more precise with a high level of confidence that you haven't introduced any "holes" for exceptional conditions to sneak out where previously they couldn't. This is essentially why we still have a few Not_found exceptions in Core, because it's really pretty hard to know where they might be relied upon, so it's easier to leave them in than purge them and risk silent breakage. --001a11c27e386f8c2904fa5dad16 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
On 2= 6 May 2014 17:02, Philippe Veber <philippe.veber@gmail.com><= /span> wrote:
Using options or Result from time to time is certainly a nice habit. However=20 to be honest, using option/result monads extensively does not seem right to me, like pulling the language in a direction it was not really=20 designed for. Monad combinators do a great job at propagating errors,=20 but still there is a serious addition of plumbing in function body's and Result returning functions have a significantly less readable type. But I guess this is pretty much a question of taste/habit to see if the=20 added verbosity is worth it.

It's t= rue that using a monadic style does introduce *some* noise, but I don't think it's so bad, especially if you des= ign=20 your API to fit with the style. In a way I almost prefer having the=20 binds there so that I can easily see where the possible interruption=20 points in the flow are, and in particular it's /syntactically/ obvious= =20 where errors /can't/ occur.

BTW c= ore still uses exceptions. Is there an explicit rule as to how to=20 decide between Result type or exceptions. For instance, why not write=20 the Array.create function like this:

val create : int -> 'a -> &#= 39;a array Or_error.t

where create = fails for a negative integer?

Daniel's summary is very good, although we do use Failure in Core, ofte= n for assertion-failure like scenarios or just any case where the error isn= 't meant to be handled specifically. In many cases Invalid_argument wou= ld arguably be more appropriate, but in other cases that isn't obvious = (say if you mutate a Queue.t while you are folding over it, that will be de= tected and an error will be thrown, but it isn't /really/ an invalid ar= gument error), and in any case since we often don't intend to catch the= m except to print a stack trace and die, it doesn't matter too much.

We also do just break our own rules sometimes. Possible reas= ons why are:
- some aspect of usability takes priority over d= ogmatics (although this is much rarer than you'd think)
- legacy exceptions (see below)
- sometimes we make bad decis= ions :(

Having no experi= ence on large projects, it is not clear to me why exceptions are considered= so dangerous. One point I see is that when you decide to handle errors wit= h a try ... with expression, there is no exhaustivity check to ensure you h= andled all possible cases, and that you have to rely on (possibly incomplet= e) documentation and reasonning to convince yourself you forgot nothing. An= other related one is that there is no automatic analysis to show exceptions= thrown by a function in the documentation. These are of course already str= ong reasons why not to use too many exceptions (and were the motivation for= my initial question). Do you see other reasons?

One particularly g= alling aspect of exceptions not showing up in types is that they can't = easily be refactored. If you decide to change which exceptions a given func= tion throws, you can't rely on the typechecker to flag up where code wa= s written to the old specification and needs changing. You can't even j= ust eyeball the code to see where the function was used and see if the exce= ption was caught, because who knows where in the call stack it might have b= een handled? Explicit error types, on the other hand, will refuse to compil= e unless you have changed your code to deal with it, so you can make your e= rrors richer or more precise with a high level of confidence that you haven= 't introduced any "holes" for exceptional conditions to sneak= out where previously they couldn't.

This is essentially why we still have a few Not_found except= ions in Core, because it's really pretty hard to know where they might = be relied upon, so it's easier to leave them in than purge them and ris= k silent breakage.
--001a11c27e386f8c2904fa5dad16--