From dev-return-5907-archive-asf-public=cust-asf.ponee.io@groovy.apache.org Mon Feb 18 01:48:26 2019 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 23E7B180657 for ; Mon, 18 Feb 2019 02:48:24 +0100 (CET) Received: (qmail 51790 invoked by uid 500); 18 Feb 2019 01:48:24 -0000 Mailing-List: contact dev-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@groovy.apache.org Delivered-To: mailing list dev@groovy.apache.org Received: (qmail 51773 invoked by uid 99); 18 Feb 2019 01:48:23 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd3-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 18 Feb 2019 01:48:23 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd3-us-west.apache.org (ASF Mail Server at spamd3-us-west.apache.org) with ESMTP id C44D2185001 for ; Mon, 18 Feb 2019 01:48:22 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd3-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -0.621 X-Spam-Level: X-Spam-Status: No, score=-0.621 tagged_above=-999 required=6.31 tests=[DKIMWL_WL_HIGH=-0.12, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=2, RCVD_IN_DNSWL_MED=-2.3, SPF_PASS=-0.001, URIBL_BLOCKED=0.001] autolearn=disabled Authentication-Results: spamd3-us-west.apache.org (amavisd-new); dkim=pass (1024-bit key) header.d=thomsonreuters.com header.b=JP3XDqxk; dkim=pass (1024-bit key) header.d=trten.onmicrosoft.com header.b=De7HO34B Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd3-us-west.apache.org [10.40.0.10]) (amavisd-new, port 10024) with ESMTP id 2J_aGTqNPqMF for ; Mon, 18 Feb 2019 01:48:19 +0000 (UTC) Received: from mailout1-trp.thomsonreuters.com (mailout1-trp.thomsonreuters.com [163.231.6.6]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with ESMTPS id EC06262454 for ; Mon, 18 Feb 2019 01:48:17 +0000 (UTC) Received: from trpusmneagrly01.int.westgroup.com (relay1 [163.231.22.97]) by mailout1-trp.thomsonreuters.com (Sentrion-MTA-4.3.1/Sentrion-MTA-4.3.1) with ESMTP id x1I1mCxY007532 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Mon, 18 Feb 2019 01:48:13 GMT X-DKIM: OpenDKIM Filter v2.4.3 mailout1-trp.thomsonreuters.com x1I1mCxY007532 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=thomsonreuters.com; s=defaultdkim; t=1550454493; bh=Tu06EVuYSIa19metybGlTRmGrSxvfTrZEY1Coa2PfMI=; h=From:To:Subject:Date:Message-ID:References:In-Reply-To: Content-Type:MIME-Version; b=JP3XDqxkOTgEO4hJK4m0butEn47exK//BUBZtWxaZ7rFFeSr0mvcfaySNRf0zcrAF t2OESugz/t5mAIao1cR/S3PI78lrkIejykDSEPkFZWuiJTckhCb3ypYmyKopxBCznL L4cDFayVg59En9HQCxWq7raLC/IL3gGkBreTd6VQ= Received: from C111MVNHHUB16.ERF.thomson.com ([10.204.103.214]) by trpusmneagrly01.int.westgroup.com (Sentrion-MTA-4.3.1/Sentrion-MTA-4.3.1) with ESMTP id x1I1mA95011560; Mon, 18 Feb 2019 01:48:11 GMT Received: from C779WDBXH301.ERF.thomson.com (10.204.96.121) by C111MVNHHUB16.ERF.thomson.com (163.231.29.140) with Microsoft SMTP Server (TLS) id 14.3.389.1; Sun, 17 Feb 2019 19:48:10 -0600 Received: from C134ZXDXM302.ERF.thomson.com (10.204.96.126) by C779WDBXH301.ERF.thomson.com (10.204.96.121) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Sun, 17 Feb 2019 19:48:09 -0600 Received: from NAM02-CY1-obe.outbound.protection.outlook.com (10.204.52.13) by C134ZXDXM302.ERF.thomson.com (10.204.96.126) with Microsoft SMTP Server (TLS) id 15.0.1367.3 via Frontend Transport; Sun, 17 Feb 2019 19:48:09 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=TRTEN.onmicrosoft.com; s=selector1-thomsonreuters-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=+UPfBVpjWkNzUOXQLrbtJKZv+WsojjPcGPeUT5ueZro=; b=De7HO34BbXAi6iWrTDhvUE41/2a7huEiu4IASfrNpCx60bqb0OQw+sUtk4Ncr9Ny8xw8FfOR/I26Fp8+6JTvvcmH5DDRDH2Wzvata1+QwdntzDYEhk0j0Wg/JtSQGxpJNM0ZW1h6qp1VIhNFwUaGUGPe6U9C5bt3hP83e9OcdqM= Received: from DM5PR03MB3161.namprd03.prod.outlook.com (10.174.190.34) by DM5PR03MB3132.namprd03.prod.outlook.com (10.174.190.29) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.1622.19; Mon, 18 Feb 2019 01:48:08 +0000 Received: from DM5PR03MB3161.namprd03.prod.outlook.com ([fe80::a167:7e0d:175f:5e8d]) by DM5PR03MB3161.namprd03.prod.outlook.com ([fe80::a167:7e0d:175f:5e8d%5]) with mapi id 15.20.1622.018; Mon, 18 Feb 2019 01:48:08 +0000 From: "Milles, Eric (TR Tech, Content & Ops)" To: Groovy_Developers , "paulk@asert.com.au" Subject: Re: DGM for first or default Thread-Topic: DGM for first or default Thread-Index: AQHUZvgkxxkdRZRHxE++kUbgSnGhS6UlNfsAgAAA1wCAAAVogIAAAqdIgAARngCAAAFMKIAAUjkAgAERCSuAvmL424AAKGyEgAAtnYCAABmxDw== Date: Mon, 18 Feb 2019 01:48:08 +0000 Message-ID: References: <1539881122104-0.post@n5.nabble.com> <80B24929-FE90-478D-B437-4A08D3877953@ocs.cz> , In-Reply-To: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=eric.milles@thomsonreuters.com; x-originating-ip: [73.65.0.163] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: c7eb075f-c62a-46cf-b0fe-08d6954321dd x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(5600110)(711020)(4605104)(2017052603328)(7153060)(7193020);SRVR:DM5PR03MB3132; x-ms-traffictypediagnostic: DM5PR03MB3132: x-microsoft-exchange-diagnostics: 1;DM5PR03MB3132;20:y5nMP3ABoJBywMRffnaSepDrLxxRGtI0J1PHzt6qENwjL882h4rKmzatDnJWXyxjNllwQKYV6lSdHmv7gUiL9rGilfCxF6o6ckRADvMaY1NYxyBpdMYt7t2OfkHc5/Y3d6iEA7gYZgCntrhgRGT/LbknEYxdwdvl7FxBsONxua7HqGJc4GvEBpdzg/GcdvyFv+ux56VmHfrJFqBMDZsn93Oj81i4Da6lfaPkruMFz2n5+GTnHmiPdHyTQALoYNryZEn1blKKkOz/Q5LvTyAaDfw123CoS2Su1E3XiiBpi9u19hFqk8QGMJYJe33vg5S8dRojo11u0aIcGDmQSwDTlWwMBocK1Sjrgr7HHqr/Pjpe8GMmBQUTxiNXyOj+eCfaLBEwi8ZGjVC197iTIcpRStaginC7g3lhdu6GVoQsuCD39tN/j8A8l9Kdo/8/Ao9ix7ESMaQFF03mBueBt0EJ1vyGXuPsHVv9bOt0WSfazTS4TmSamn1x6yhI+PKDqQMF x-microsoft-antispam-prvs: x-forefront-prvs: 09525C61DB x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(396003)(366004)(39860400002)(346002)(376002)(136003)(189003)(51914003)(199004)(99286004)(14444005)(8936002)(5660300002)(229853002)(316002)(256004)(110136005)(76176011)(7696005)(81156014)(3846002)(6116002)(8676002)(81166006)(5070765005)(486006)(476003)(561944003)(33656002)(93886005)(236005)(68736007)(186003)(6246003)(54896002)(11346002)(53936002)(9686003)(53946003)(55016002)(25786009)(26005)(19627405001)(6606003)(446003)(478600001)(6506007)(6436002)(102836004)(86362001)(14454004)(2501003)(66066001)(53546011)(30864003)(71190400001)(71200400001)(97736004)(105586002)(74316002)(2906002)(19627235002)(106356001)(7736002)(461764006);DIR:OUT;SFP:1101;SCL:1;SRVR:DM5PR03MB3132;H:DM5PR03MB3161.namprd03.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;MX:1;A:1; received-spf: None (protection.outlook.com: thomsonreuters.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: KPfxRjkkig9JTZUHVTH5QBANTMFTDFuxOrked6tJtyRDkOTGXxFEP91A29QxMAZqdLJWU4drbMyJHyE3u9xCf26wS8OmY0NtD6I2e7ywj3q4vR4+KaDyCMVAo9FYQ3cLx7KiyDkoTRhabDxn3vaQar6ybpWHo6gh2HSMa+KiVD3Ix47NwdD5MtCoWupS/QUGNmbHRiopBLnf6xwOVoou4hRr6OJhiuOt8sFtv8aprGSOb22eUrEBd8NVnRk1/AZwZbUMEc2MQHsm1xyCO8zNE3MH//JBKv0ujx80JNgRh669lEkwrsKo4Ylddifz0XGf2wqx89d5L6GKvb8j1hzCNbJ2beZEYuenOFjPNU4harP90OA+diSe15Kjl91a49pMwKrSwBYzW1WY+nVoyErg4i3QpPJzTMMH/eseyCUDUzY= Content-Type: multipart/alternative; boundary="_000_DM5PR03MB3161A8A7FF37F8A2189C3CEC8A630DM5PR03MB3161namp_" MIME-Version: 1.0 X-MS-Exchange-CrossTenant-Network-Message-Id: c7eb075f-c62a-46cf-b0fe-08d6954321dd X-MS-Exchange-CrossTenant-originalarrivaltime: 18 Feb 2019 01:48:08.1867 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 62ccb864-6a1a-4b5d-8e1c-397dec1a8258 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM5PR03MB3132 X-OriginatorOrg: thomsonreuters.com X-TM-AS-Product-Ver: SMEX-12.0.0.1727-8.200.1013-24434.007 X-TM-AS-Result: No--5.742900-0.000000-31 X-TM-AS-MatchedID: 143723-150567-700107-709253-700433-710739-702358-703720-7 03712-700324-705861-706719-704425-700470-705461-708339-701182-704746-106660 -702609-703938-704421-186035-703788-705526-709584-705388-700401-700022-7022 64-700075-703096-707909-700038-121111-113232-700693-188019-111804-700398-70 1298-188198-701588-780022-105700-700726-710970-701437-707163-703747-710989- 700551-701594-303277-701959-105250-702113-707654-707997-706561-704342-70983 5-709397-708797-706871-707915-701428-701384-700846-706023-702796-700473-702 196-701465-701163-703304-705528-709908-700151-708752-700264-705901-707760-7 05424-711432-702020-702187-700537-702114-700463-148004-148007-148020-148036 -148051-25004-42003-63 X-TM-AS-User-Approved-Sender: Yes X-TM-AS-User-Blocked-Sender: No X-CFilter-Loop: Reflected --_000_DM5PR03MB3161A8A7FF37F8A2189C3CEC8A630DM5PR03MB3161namp_ Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Thanks for the note on 2.5 and the v8 methods. I considered withDefault, but yes the mutation was a deal breaker and it on= ly works for List and Map. If I use Optional, then the case of null as a m= ember of the collection becomes an issue to deal with. Adding a bevy of ne= w DGMs is not ideal either. This is a tough one; I might end up sticking w= ith "iterable ? iterable.first() : defaultValue" for now. I would like to submit for future consideration that it would be nice to be= able to control getAt's choice to return null if the given index is out of= range. This to me is the more general solution. ________________________________ From: Paul King Sent: Sunday, February 17, 2019 6:07 PM To: Groovy_Developers Subject: Re: DGM for first or default Groovy 2.5 still has JDK 7 as minimum whereas Groovy 3.0 has 8 as minimum. = You can still add DGM methods into org.codehaus.groovy.vmplugin.v8.PluginDe= faultGroovyMethods and then they will only be available when running on a J= DK8+ VM. Wrt to the question of whether we should have firstOrElse, lastOrElse, getA= tOrElse, etc., it is certainly a possibility to add such a whole myriad of = duplicated methods but there might be better alternatives. Does the existin= g ListWtihDefault work for you? It does mutate which might not be what you = are after but perhaps we could create an alternative variant of that. Cheers, Paul. On Mon, Feb 18, 2019 at 7:36 AM Milles, Eric (TR Tech, Content & Ops) > wrote: When I first worked all this up my target was Groovy 2.4. If that is raise= d to Groovy 2.5+ I just started to wonder if java.util.Optional could be le= veraged for an improved solution. I also remembered some feedback some tim= e ago from MG where to no-arg call would be "getName()" so that it could be= used in Groovy sources as "name". Then if there were versions with more a= rguments, they would be "name(arg1, arg2, ...)". So if I were to implement new DGMs in a vacuum, would something like this m= ake sense? public static Optional getFirst(Iterable self) public static Optional getFirst(List self) public static Optional getFirst(T[] self) public static Optional getFirst(org.codehaus.groovy.runtime.NullObje= ct self) This could be used like "list.first.orElse(defaultValue)". Now that I writ= e that out, I can see the conflict with list spread shorthand. Darn, I wis= h that didn't exist and you had to explicitly use "list*.prop". General question: Are there limitations to using Java 8 stuff in Groovy cor= e? I'm not sure how Android support comes into play here. ________________________________ From: Milles, Eric (TR Tech, Content & Ops) > Sent: Sunday, February 17, 2019 1:56 PM To: dev@groovy.apache.org Subject: Re: DGM for first or default I'd like to restart this discussion. I'm not sure if this should move to n= ew JIRA ticket or Pull Request. There are a few open questions I'd like to= get some feedback on. I have managed to solve the problem of getting from= a null array/iterable. My use case is getting the first item from an iterable or else a supplied d= efault. Alternate proposals like "list?.first() ?: defaultValue" or "list?= .find() ?: defaultValue" do not properly handle all use cases. It took qui= te a number of tries to figure it out. "list ? list.first() : defaultValue" appears to be roughly equivalent. How= ever, having the additional DGMs would mean a one-time check for non-null, = non-empty and elimination of repeated "list", which may be a more complicat= ed expression. And in rare cases, a type could extend Iterable and provide= a non-standard implementation of asBoolean(). Proposed additions to DefaultGroovyMethods: public static T firstOrElse(Iterable self, T defaultValue) public static T firstOrElse(List self, T defaultValue) // allows use= of isEmpty() and get(i) instead of creating an Iterator instance public static T firstOrElse(T[] self, T defaultValue) public static T firstOrElse(org.codehaus.groovy.runtime.NullObject self= , T defaultValue) // exists solely for the (null).firstOrDefault(value) cas= e public static T firstOrElse(Iterable self, Supplier defaultValue) public static T firstOrElse(List self, Supplier defaultValue) public static T firstOrElse(T[] self, Supplier defaultValue) public static T firstOrElse(org.codehaus.groovy.runtime.NullObject self= , Supplier defaultValue) Since this is targeted at Groovy 2.5+ I have selected java.util.function.Su= pplier instead of groovy.lang.Closure. Although that could be changed if a= new Groovy 2.4 minor release was planned. Usage: Iterable iterable =3D null println iterable.firstOrElse('default') // prints 'default' iterable =3D [] println iterable.firstOrElse('default') // prints 'default' iterable =3D [0] println iterable.firstOrElse('default') // prints 0 iterable =3D [null] println iterable.firstOrElse('default') // prints null iterable =3D [false] println iterable.firstOrElse('default') // prints false iterable =3D null iterable.firstOrElse { -> throw new ExceptionOfMyChoosing() } // throws Open items: 1. Should a set of methods "lastOrElse" also be created? It would not t= ake much effort to add them at the same time. 2. Should the "getAt" methods be similarly extended, like "public static= T getAt(List self, int idx, T defaultValue)", etc. 3. If "getAt" is extended in this way, would it be useful to also consid= er extending the "[idx]" syntax form of "getAt" to include the default? 4. There was a question raised: if "iterable" contains Closures or Suppl= iers, how should that case be handled? I'm curious how often this might co= me up. ________________________________ From: Milles, Eric (TR Technology & Ops) > Sent: Friday, October 19, 2018 10:54 AM To: dev@groovy.apache.org Subject: Re: DGM for first or default These may seem a bit simple for DGMs, but they do solve a couple problems a= s compared to "list ? list.first() ?: defaultValue". First, no repetition = of the "list" expression, which may be complex. Second, content assist wil= l show proposals for these methods. Third, the Groovy truth problems with = "list?[0] ?: defaultValue", "list?.getAt(0) ?: defaultValue", "list.find() = ?: defaultValue" and others do not exist for these. If the first element i= s null or otherwise falsy, it will be returned as desired. The only case f= or me that is unresolved is a null array or iterable. In this case, Groovy= throws "Cannot invoke method firstOrElse() on null obect" instead of runni= ng the method allowing null tolerance and return of default value. public static T firstOrElse(Iterable self, T defaultValue) { Iterator iter =3D self.iterator(); if (iter.hasNext()) { return iter.next(); } return defaultValue; } public static T firstOrElse(T[] self, T defaultValue) { if (self.length > 0) { return self[0]; } return defaultValue; } // and similarly for the Closure (or java.util.function.Supplier if Jav= a 8+ only) variants Since safe navigation is being explored for by-index access, is there a pos= sibility for including some for of "or supplied default" in any of the safe= -navigation cases? I personally find the new safe-indexing syntax to be un= necessary when "list?.getAt(i)" appears to be the equivalent to "list?[i]". Alternate proposal, what if the DGM.getAt(self, int idx) had variants that = included a default value return instead of hard-coded null? Like this: public static T getAt(List self, int idx, T def) { int size =3D self.size(); int i =3D normaliseIndex(idx, size); if (i < size) { return self.get(i); } else { //return null; return def; } } ________________________________ From: Mario Garcia > Sent: Thursday, October 18, 2018 6:19 PM To: dev@groovy.apache.org Subject: Re: DGM for first or default Good point OC: [0,'',[],[:]].find()?:'not quite what you wanted here' [0,1,2].find()?:'nor in this case' The more I think on this the more I think is an interesting topic. I fully = understand your frustration with first(), but apart from the example with C= ocoa you mentioned, looking in the JVM it turns out there're plenty of exam= ples of language collections behaving that way: In scala the head of an empty list does throw an exception ------------------- scala> var empty =3D List[Int]() empty: List[Int] =3D List() scala> empty.head java.util.NoSuchElementException: head of empty list at scala.collection.immutable.Nil$.head(List.scala:426) at scala.collection.immutable.Nil$.head(List.scala:423) ... 28 elided scala> --------------------- and so does kotlin when calling to first() ---------------------- Welcome to Kotlin version 1.2.71 (JRE 1.8.0_171-b11) Type :help for help, :quit for quit >>> val num: List =3D listOf() >>> num.first() java.util.NoSuchElementException: List is empty. at kotlin.collections.CollectionsKt___CollectionsKt.first(_Collections.kt:1= 84) >>> --------------------- in Kotlin they have firstOrNull(), but I haven't found any overloaded funct= ion with a default value. They also have "find", but it's not possible to c= all it without parameter However Clojure returns null whether: * The first element was nil * The list was empty * Or the list was nil -------------------- user=3D> (def a nil) #'user/a user=3D> a nil user=3D> (first a) nil user=3D> (def a '(nil)) #'user/a user=3D> a (nil) user=3D> (first a) nil user=3D> (def a '()) #'user/a user=3D> a () user=3D> (first a) nil user=3D> ------------------- BTW I forgot to mention that Groovy 3 will have safe indexing meaning an ex= pression like the following: * will return the first element of a non empty list which I guess it wi= ll be the Kotlin firstOrNull() equivalent * or null if the list was null or empty --------- // trying to get first element from null list nullList?[0] =3D=3D> null // trying to get an inexistent element from a non empty list (but this is n= ot new, this is how a non empty list indexing works in Groovy) nonNullList?[9999] =3D> null ---------- Outside the JVM, Haskell, when asking for the head of an empty list, throws= an exception (There is an explanation in stackoverflow which I'm afraid I = don't understand). So in the end Groovy's first() seems not to be the excep= tion among other modern languages out there. Another point of view, could be thinking about returning null consistently.= Lets say a list returns null using first(): * Does it mean the first element is a null value or is an empty list an= d that's why is giving me a null value ? * What if null is a valid value, with some meaning in my process ? With= that context a method like firstOrNull() (even first(defaultValue) with a = null list) could be considered ambiguous. My guess is that in the case of languages throwing an exception using first= () on an empty list, when they designed the language collections they didn'= t have any other way to express that kind of semantics. But this is just a = lucky guess. I'm probably wrong. I only can think of pattern matching as a = complete solution, where the terminal result in the case of an empty or nul= l list, is a default value different than any of the elements of the expect= ed result set ? I apologize in advance for the lengthy e-mail, but it seemed interesting to= think why first() was designed like that, not only in Groovy, but in some = other languages as well. Mario El jue., 18 oct. 2018 a las 20:27, Milles, Eric (TR Technology & Ops) (>) escrib= i=F3: I think first() exists so there is a semantic pair for functional programmi= ng: first()/head() and tail() or init() and last() ________________________________ From: ocs@ocs > Sent: Thursday, October 18, 2018 1:20 PM To: dev@groovy.apache.org Subject: Re: DGM for first or default Well I thought first is smart enough to return null for an empty list, same= as my firstObject in Cocoa does. If it throws, what's on earth point of ha= ving the thing at all? In that case it can be replaced by list[0] without a= ny drawback at all. All the best, OC On 18 Oct 2018, at 7:19 PM, Milles, Eric (TR Technology & Ops) > wrote: "list?.first() ?: defaultValue" is not the equivalent. If the collection i= s empty, first() throws an IndexOutOfBoundsException is thrown. That's why= I'm asking if there is a simple equivalent. I suppose this is the equival= ent now that I think about it: list ? list.first() : defaultValue ________________________________ From: ocs@ocs > Sent: Thursday, October 18, 2018 12:07 PM To: dev@groovy.apache.org Subject: Re: DGM for first or default Myself, I am not a huge fan of adding not-often-needed functionalities (and= actually would add almost none of those discussed lately); nevertheless... On 18 Oct 2018, at 6:48 PM, Paolo Di Tommaso > wrote: -1, it can be easily done as: list.first() ?: defaultValue ... this won't work in case the first object is a Groovy False (e.g., an em= pty string, or a plethora of others). --_000_DM5PR03MB3161A8A7FF37F8A2189C3CEC8A630DM5PR03MB3161namp_ Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable

Thanks for the note on 2.5 and th= e v8 methods.


I considered withDefault, but yes= the mutation was a deal breaker and it only works for List and Map.  = If I use Optional, then the case of null as a member of the collection beco= mes an issue to deal with.  Adding a bevy of new DGMs is not ideal either.  This is a tough one; I might end up= sticking with "iterable ? iterable.first() = : defaultValue" for now.


I would like to submit for fut= ure consideration that it would be nice to be able to control getAt's = choice to return null if the given index is out of range.  This to me is the more general solution.



From: Paul King <paulk@a= sert.com.au>
Sent: Sunday, February 17, 2019 6:07 PM
To: Groovy_Developers
Subject: Re: DGM for first or default
 
Groovy 2.5 still has JDK 7 as minimum whereas Groovy 3.0 h= as 8 as minimum. You can still add DGM methods into org.codehaus.groov= y.vmplugin.v8.PluginDefaultGroovyMethods and then they will only be availab= le when running on a JDK8+ VM.

Wrt to the question of whether we should have firstOrElse, lastOrElse,= getAtOrElse, etc., it is certainly a possibility to add such a whole myria= d of duplicated methods but there might be better alternatives. Does the ex= isting ListWtihDefault work for you? It does mutate which might not be what you are after but perhaps we c= ould create an alternative variant of that.

Cheers, Paul.


On Mon, Feb 18, 2019 at 7:36 AM Mil= les, Eric (TR Tech, Content & Ops) <eric.milles@thomsonreuters.com> wrote:

When I first worked all this= up my target was Groovy 2.4.  If that is raised to Groovy 2.5+ I = just started to wonder if java.util.Optional could be leveraged for an improved solution.  I also remembered = some feedback some time ago from MG where to no-arg call would be "get= Name()" so that it could be used in Groovy sources as "name".  Then if there were versions with more arguments= , they would be "name(arg1, arg2, ...)".


So if I were to implement ne= w DGMs in a vacuum, would something like this make sense?


public static <T> Optional= <T> getFirst(Iterable<T> self)

public static <T> Optional<= T> getFirst(List<T> self)

publ= ic static <T> Optional<T> getFirst(T[] self)
public static <T> Option= al<T> getFirst(org.codehaus.groovy.runtime.NullObject self= )

This could be used like "list.first.orElse(defaultValue)".&n= bsp; Now that I write that out, I can see the conflict with list spread sho= rthand.  Darn, I wish that didn't exist and you had to explicitly use = "list*.prop".


General question: Are there limitations to using Java 8 stuff i= n Groovy core?  I'm not sure how Android support comes into play here.=



Fr= om: Milles, Eric (TR Tech, Content & Ops) <eric.milles@thomsonreuters.com>=
Sent: Sunday, February 17, 2019 1:56 PM
To: dev@groovy.apache.org
Subject: Re: DGM for first or default
 

I'd like to restart this dis= cussion.  I'm not sure if this should move to new JIRA ticket or = Pull Request.  There are a few open questions I'd like to get some fee= dback on.  I have managed to solve the problem of getting from a null array/iterable.


My use case is getting the f= irst item from an iterable or else a supplied default.  Alternate prop= osals like "list?.first() ?: defaultValue" or "list?.find() ?: defaultValue" do not properly hand= le all use cases.  It took quite a number of tries to figure it o= ut.


"list ? list.first() : defaultValue" appears to be roughly equ= ivalent.  However, having the additional DGMs would mean a one-time check for non-null, non-empty and elimination of rep= eated "list", which may be a more complicated expression.  A= nd in rare cases, a type could extend Iterable and = provide a non-standard implementation of asBoolean().<= /span>



Proposed additions to Def= aultGroovyMethods:


public static <T> T firstO= rElse(Iterable<T> self, T defaultValue)

public static <T> T firstOrEls= e(List<T> self, T defaultValue) // allows use of isEmpty() and get(i) instead of creating an Iterator instance

public static <T> T firstO= rElse(T[] self, T defaultValue)

public static <T&= gt; T firstOrElse(org.codehaus.groovy.runtime.NullObject self,= T defaultValue) // exists solely for the (null).firstOrDefault(value)= case


public static <T&= gt; T firstOrElse(Iterable<T> self, Supplier<T> defa= ultValue)

public static <T&= gt; T firstOrElse(List<T> self, Supplier= <T> defaultValue)

public static <T&= gt; T firstOrElse(T[] self, Supplier= <T> defaultValue)

public static <T&= gt; T firstOrElse(org.codehaus.groovy.runtime.NullObject self, Supp= lier<T> defaultValue)


Since this is targ= eted at Groovy 2.5+ I have selected java.util.function.Supplier instead of = groovy.lang.Closure.  Although that could be changed if a new Groovy 2= .4 minor release was planned.


Usage:
  Iterable= <Object> iterable =3D null
  println = iterable.firstOrElse('default') // prints 'default'

  iterable= =3D []
  println = iterable.firstOrElse('default') // prints 'default'

  iterable= =3D [0]
  println = iterable.firstOrElse('default') // prints 0

  iterable= =3D [null]
  println = iterable.firstOrElse('default') // prints null

  iterable= =3D [false]
  println = iterable.firstOrElse('default') // prints false

  iterable= =3D null
  iterable= .firstOrElse { -> throw new ExceptionOfMyChoosing() } // throws



Open items:

  1. Should a set of methods "lastOrElse" also be created?  I= t would not take much effort to add them at the same time.
  2. Should t= he "getAt" methods be similarly extended, like "public= static <T> T getAt(List<T> self, int idx, T defaultValue)", etc.
  3. If "getAt" is extended in this way, would = it be useful to also consider extending the "[idx]" syntax form o= f "getAt" to include the default? 
  4. There was a quest= ion raised: if "iterable" contains Closures or Suppliers, how sho= uld that case be handled?  I'm curious how often this might come up.




= From: Milles, Eric (TR Technology & Ops) <eric.milles@thomsonreuters.com><= br> Sent: Friday, October 19, 2018 10:54 AM
To: dev@groovy.apache.org
Subject: Re: DGM for first or default
 

These may seem a bit simple = for DGMs, but they do solve a couple problems as compared to "list ? l= ist.first() ?: defaultValue".  First, no repetition of the "= list" expression, which may be complex.  Second, content assist will show proposals for these methods.  Third, the Groovy trut= h problems with "list?[0] ?: defaultValue", "list?.getAt(0) ?: defaultValue", "list.find() ?: defaultValue" and = others do not exist for these.  If the first element is null or otherw= ise falsy, it will be returned as desired.  The only case for me that = is unresolved is a null array or iterable.  In this case, Groovy throws "Cannot invoke method firstOrElse() on null obect"= instead of running the method allowing null tolerance and return of defaul= t value.


    p= ublic static <T> T firstOrElse(Iterable<T> self, T defaultValue= ) {
    &= nbsp;   Iterator<T> iter =3D self.iterator();
    &= nbsp;   if (iter.hasNext()) {
    &= nbsp;       return iter.next();
    &= nbsp;   }
    &= nbsp;   return defaultValue;
    }=
    p= ublic static <T> T firstOrElse(T[] self, T defaultValue) {
    &= nbsp;   if (self.length > 0) {
    &= nbsp;       return self[0];
    &= nbsp;   }
    &= nbsp;   return defaultValue;
    }=
    /= / and similarly for the Closure (or java.util.function.Supplier if Java 8+ only) variants



Since safe navigation is being explored for by-index access, is the= re a possibility for including some for of "or supplied default" = in any of the safe-navigation cases?  I personally find the new&nb= sp;safe-indexing syntax to be unnecessary when "list?.getAt(i)" appears to be the equivalent to "list?[i]".


Alternate proposal, what if the DGM.getAt(self, int idx) had varian= ts that included a default value return instead of hard-coded null?&nbs= p; Like this:

    p= ublic static <T> T getAt(List<T> self, int idx, T def) {
    &= nbsp;   int size =3D self.size();
    &= nbsp;   int i =3D normaliseIndex(idx, size);
    &= nbsp;   if (i < size) {
    &= nbsp;       return self.get(i);
    &= nbsp;   } else {
    &= nbsp;       //return null;
    &= nbsp;       return def;
    &= nbsp;   }
    }=




<= b>From: Mario Garcia <mario.ggar@gmail.com>
Sent: Thursday, October 18, 2018 6:19 PM
To: dev@groovy.apache.org
Subject: Re: DGM for first or default
 
Good point OC:

[0,'',[],[:]].find()?:'not quite what you wanted here'
[0,1,2].find()?:'nor in this case'

The more I think on this the more I think is an interestin= g topic. I fully understand your frustration with first(), but apart from t= he example with Cocoa you mentioned, looking in the JVM it turns out there'= re plenty of examples of language collections behaving that way:

In scala the head of an empty list does throw an exception
-------------------
scala> var empty =3D List[Int]()
empty: List[Int] =3D List()

scala> empty.head
java.util.NoSuchElementException: head of empty list
  at scala.collection.immutable.Nil$.head(List.scala:426)
  at scala.collection.immutable.Nil$.head(List.scala:423)
  ... 28 elided

scala> 
---------------------

and so does kotlin when calling to first()
----------------------
Welcome to Kotlin version 1.2.71 (JRE 1.8.0_171-b11)
Type :help for help, :quit for quit
>>> val num: List<Int> =3D listOf()
>>> num.first()
java.util.NoSuchElementException: List is empty.
at kotlin.collections.Coll= ectionsKt___CollectionsKt.first(_Collections.kt:184)
>>> 
---------------------
in Kotlin they have firstOrNull(), but I haven't found any overloaded = function with a default value. They also have "find", but it= 's not possible to call it without parameter

However Clojure returns null whether:
  • The first element was nil
  • The list was empty
  • Or the list was nil
--------------------
user=3D> (def a nil)
#'user/a
user=3D> a
nil
user=3D> (first a)
nil
user=3D> (def a '(nil))
#'user/a
user=3D> a
(nil)
user=3D> (first a)
nil
user=3D> (def a '())
#'user/a
user=3D> a
()
user=3D> (first a)
nil
user=3D> 
-------------------

BTW I forgot to mention that Groovy 3 will have safe indexing meaning = an expression like the following:
  • will return the first element of a non empty list which I guess it will= be the Kotlin firstOrNull() equivalent
  • or null if the list was null or empty
---------
// trying to get first element from null list
nullList?[0] =3D=3D> null

// trying to get an inexistent element from a non empty list (but this= is not new, this is how a non empty list indexing works in Groovy)
nonNullList?[9999] =3D> null
----------

Outside the JVM, Haskell, when asking for the head of an empty list, t= hrows an exception (There is an explanation in stackoverflow which I'm afra= id I don't understand). So in the end Groovy's first() seems not to be the = exception among other modern languages out there.

Another point of view, could be thinking about returning null consiste= ntly. Lets say a list returns null using first():
  • Does it mean the first element is a null value or is an empty list and = that's why is giving me a null value ? 
  • What if null is a valid value, with some meaning in my process ? W= ith that context a method like firstOrNull() (even first(defaultValue) with= a null list) could be considered ambiguous.
My guess is that in the case of languages throwing an exception using = first() on an empty list, when they designed the language collections they = didn't have any other way to express that kind of semantics. But this is ju= st a lucky guess. I'm probably wrong. I only can think of pattern matching as a complete solution, where the ter= minal result in the case of an empty or null list, is a default value diffe= rent than any of the elements of the expected result set ? 

I apologize in advance for the lengthy e-mail, but it seemed inte= resting to think why first() was designed like that, not only in Groovy, bu= t in some other languages as well.
Mario

El jue., 18 oct. 2018 a las 20:27, Milles, Eric (TR Techno= logy & Ops) (<eric.milles@thomsonreute= rs.com>) escribi=F3:

I think first() exists so th= ere is a semantic pair for functional programming: first()/head() and = tail()  or init() and last()



From: ocs@ocs <ocs@ocs.cz>
Sent: Thursday, October 18, 2018 1:20 PM
To: dev@groovy.apache.org
Subject: Re: DGM for first or default
 
Well I thought first is smart enough to return = null for an empty list, same as my firstObject in Cocoa does. If it throws, what's on earth point = of having the thing at all? In that case it can be replaced by list[0] without any drawback at all.

All the best,
OC

On 18 Oct 2018, at 7:19 PM, Milles, Eric (TR Technology & Ops) <= ;eric.milles@thomsonreuters.com> wrote:

"list?.first() ?: def= aultValue" is not the equivalent.  If the collection is empty, fi= rst() throws an IndexOutOfBoundsException is thrown.  That's why = I'm asking if there is a simple equivalent.  I suppose this is the equivalent now that I think about it:

list ? list.first() : defaultValue



From: ocs@ocs <ocs@ocs.cz>
Sent: Thursday, October 18, 2018 1= 2:07 PM
To: dev@groovy.apach= e.org
Subject: Re: DGM for first or defa= ult
 
Myself, I am not a huge fan of adding not-often-needed func= tionalities (and actually would add almost none of those discussed lately);= nevertheless...

On 18 Oct 2018, at 6:48 PM, Paolo Di Tommaso <pa= olo.ditommaso@gmail.com> wrote:

-1, it can be easily done as: 
list.first() ?: defaultValue

... this won't work in case the first object is a Groovy False (e.g., = an empty string, or a plethora of others).

--_000_DM5PR03MB3161A8A7FF37F8A2189C3CEC8A630DM5PR03MB3161namp_--