From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp11.migadu.com ([2001:41d0:8:6d80::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms0.migadu.com with LMTPS id gLxyKJ0P2WH0kQAAgWs5BA (envelope-from ) for ; Sat, 08 Jan 2022 05:14:21 +0100 Received: from aspmx1.migadu.com ([2001:41d0:8:6d80::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp11.migadu.com with LMTPS id MAjAJZ0P2WFE1AAA9RJhRA (envelope-from ) for ; Sat, 08 Jan 2022 05:14:21 +0100 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 90F6D2F462 for ; Sat, 8 Jan 2022 05:14:20 +0100 (CET) Received: from localhost ([::1]:35564 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1n637T-0008HD-43 for larch@yhetil.org; Fri, 07 Jan 2022 23:14:19 -0500 Received: from eggs.gnu.org ([209.51.188.92]:42504) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1n637D-0008Gq-As for guix-patches@gnu.org; Fri, 07 Jan 2022 23:14:03 -0500 Received: from debbugs.gnu.org ([209.51.188.43]:53284) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1n637C-0003CX-47 for guix-patches@gnu.org; Fri, 07 Jan 2022 23:14:02 -0500 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1n637B-0001Se-U4 for guix-patches@gnu.org; Fri, 07 Jan 2022 23:14:01 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#51838] [PATCH v8 03/41] guix: node-build-system: Add JSON utilities. Resent-From: Philip McGrath Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Sat, 08 Jan 2022 04:14:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 51838 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: Liliana Marie Prikler Cc: 51838@debbugs.gnu.org, Timothy Sample , Pierre Langlois , Jelle Licht , Leo Famulari Received: via spool by 51838-submit@debbugs.gnu.org id=B51838.16416152355594 (code B ref 51838); Sat, 08 Jan 2022 04:14:01 +0000 Received: (at 51838) by debbugs.gnu.org; 8 Jan 2022 04:13:55 +0000 Received: from localhost ([127.0.0.1]:46186 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1n6375-0001S9-7I for submit@debbugs.gnu.org; Fri, 07 Jan 2022 23:13:55 -0500 Received: from mail-ua1-f49.google.com ([209.85.222.49]:42951) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1n636z-0001Rs-MD for 51838@debbugs.gnu.org; Fri, 07 Jan 2022 23:13:53 -0500 Received: by mail-ua1-f49.google.com with SMTP id p1so13762467uap.9 for <51838@debbugs.gnu.org>; Fri, 07 Jan 2022 20:13:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=philipmcgrath.com; s=google; h=message-id:date:mime-version:user-agent:subject:content-language:to :cc:references:from:in-reply-to:content-transfer-encoding; bh=TaYdWiEaLzamElvSiebHDJRXPry8kXqU2gJxQMHIL0g=; b=SLBzrnfaCbqhs69dAx75ZCqf5rVdXg68A9B4d681aEceSZvvEF/fkPIzbFzBerMjiS apEyoaQXhP88fwztpfdvTqoRgzErRVdld2q7WVK7DbyDsydJbGJoMkdV8a9Bdqc2vgLy l2nqdc0qO9CTcQJEXmY/p7HMFXtX2jyt3E++sgl7+dmZJejKz5F7gpzm8QFMaXBtd623 xs/tqFPgvz98PlJNRmqhHBA3zei+4QB3YCPI3vT21KGl9W1afLAWTkfa5gKpQ6TL9Gbe 9e0KxvLsqhIs6ZvO7qCbOjQ7tivL67BRc2j/dAcLT8SMH/Br5HktwR1ArAvW0Rm8byHs XXEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:date:mime-version:user-agent:subject :content-language:to:cc:references:from:in-reply-to :content-transfer-encoding; bh=TaYdWiEaLzamElvSiebHDJRXPry8kXqU2gJxQMHIL0g=; b=juYRkuC+rNwCjqzS8hBk0n0VmPZ04W21bbtfxoKxvSdoiDDfGuJIfDgPFGSHYLzR3n 9weuQSk4ATExC0B+9kAi3Eb544qcP2jEuRuUIJDHFwWI2aqAhe48sUxfhuWr7fWfIY0D b7cdF4pgcy7e/m90mhPUl+M4R9d/okWCJoOLvB1FsKDWWA7Iu24tTp5b+VVoTciFzZ9S qCA99Th0U/EWUqHUTQZTaI/9WxgOHp5uYZbkD2GEu5pORT3ik0ohkmqrqdJTQzaynd/7 gOlDilGMQJ2plKyrTHkBiEIp+fL9Bn6OfDRiLdpwlsvf7MDOzsplNdMYJIWgo5ogAPuQ B1Gw== X-Gm-Message-State: AOAM533aGPGlfHEuspWJ1B2rDAT5CgB5jiqh6kb8epBR7jMvM/U9XSdf RVqKhOB+NwAduKSwR4m3IG/LaEmu6pu1KdEe X-Google-Smtp-Source: ABdhPJxCmXJbA7h6C3iEIrhap7mrTYi75U57HXREF9O5A5+AaddN6UOFq9CsI35l2dt05mKhYtlsiQ== X-Received: by 2002:a05:6102:316e:: with SMTP id l14mr22339244vsm.8.1641615224171; Fri, 07 Jan 2022 20:13:44 -0800 (PST) Received: from [192.168.45.37] (c-73-125-89-242.hsd1.fl.comcast.net. [73.125.89.242]) by smtp.gmail.com with ESMTPSA id u69sm361600vke.30.2022.01.07.20.13.43 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 07 Jan 2022 20:13:43 -0800 (PST) Message-ID: Date: Fri, 7 Jan 2022 23:13:42 -0500 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.3.1 Content-Language: en-US References: <71ee87241c6c8a2a49c7cff916b7e1e9d508e020.camel@gmail.com> From: Philip McGrath In-Reply-To: <71ee87241c6c8a2a49c7cff916b7e1e9d508e020.camel@gmail.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: "Guix-patches" X-Migadu-Flow: FLOW_IN X-Migadu-Country: US ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1641615261; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding:resent-cc: resent-from:resent-sender:resent-message-id:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=TaYdWiEaLzamElvSiebHDJRXPry8kXqU2gJxQMHIL0g=; b=bfEsvjoSi3VeU1JG9C9L6E1B+7mXwOePgj7W7o20CWDXxtMifcatWmPaUpLMummraP/FQC AH8cEm5Sf0IqbBE6uZR0aqXMMKhGvB5xu5ZZoONjAvQQx+cqIcgoW/UQnjBAsTRRqSlb8E KO103Maql7tWCw4LESZulRi2zy19U01eiUnrSGeM7GRioaKUWwnLf4XOMlUA011yDW1uFZ gXSUlq90OG/OKEXcLAKJd5N80mjnxBkYXbGzyp4FhUd8yUwS37ouUpaJ59nTkEe6qzQ34l vr0hbErTjpj3Bac1LGk13i26B4Nz3BqcPnxh1Jlb2e71iXK4uPnmYcJdUtrCkw== ARC-Seal: i=1; s=key1; d=yhetil.org; t=1641615261; a=rsa-sha256; cv=none; b=abqbgaclsDD6APlopNBz0ExvOtfqSyTnqnenND0uP0lDFTrni3LNeGY4OGNDGklvaIdf9L yWN4kaXGkp/ei1UCXfdUIOyLkL4pKmvsI3If5UKxg96Y/PzC+2s4No4rVY6OHfKa6F954C x93gYCle9Tz7MA0OosuPYa/hYa5qY+alVAFP+diR1tYWLFhZLNud1LlhQwx5qqissX5UVS 5iJHg3pvPMpLCWuUCbvZ6TWaD9m7YlKDj0MBIxZiieSmTuQ4emCgRmxFY8Hq8U3yXwKsM+ LmuBKoiDsjez7jKmBGh8sL1AVyUImy1DwIE8/7qrvXs0bVwXQ7qDWb/Zg1aIiQ== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=philipmcgrath.com header.s=google header.b=SLBzrnfa; dmarc=none; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org" X-Migadu-Spam-Score: -0.60 Authentication-Results: aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=philipmcgrath.com header.s=google header.b=SLBzrnfa; dmarc=none; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org" X-Migadu-Queue-Id: 90F6D2F462 X-Spam-Score: -0.60 X-Migadu-Scanner: scn1.migadu.com X-TUID: cixDjNtkcugh Hi, (None of the comments in this email should block these patches, IMO. I wouldn't change any of them until we move the functions to (guix build json-utils).) On 12/30/21 02:38, Liliana Marie Prikler wrote: > This commit adds several utility functions for non-destructive > transformation of the JSON representation used by (guix build json), > particularly for purely functional update of JSON objects. They ought > to eventually be exported from their own module, but for now are kept > private to allow experimentation. > > * guix/build/node-build-system.scm (assoc-ref*, jsobject-ref, alist-pop) > (alist-update, jsobject-update*, jsobject-union): New variables. > (with-atomic-json-file-replacement): New public variable. > (module-name, build, patch-dependencies): Use them. Do not resort to > unsafe alist primitives from Guile core. > > Co-authored-by: Liliana Marie Prikler > --- > guix/build/node-build-system.scm | 145 ++++++++++++++++++++++++------- > 1 file changed, 115 insertions(+), 30 deletions(-) > > diff --git a/guix/build/node-build-system.scm b/guix/build/node-build-system.scm > index 2d7a3bdc67..c6602b876b 100644 > --- a/guix/build/node-build-system.scm > +++ b/guix/build/node-build-system.scm > @@ -3,6 +3,7 @@ > ;;; Copyright © 2016, 2020 Jelle Licht > ;;; Copyright © 2019, 2021 Timothy Sample > ;;; Copyright © 2021 Philip McGrath > +;;; Copyright © 2021 Liliana Marie Prikler > ;;; > ;;; This file is part of GNU Guix. > ;;; > @@ -26,14 +27,101 @@ (define-module (guix build node-build-system) > #:use-module (ice-9 ftw) > #:use-module (ice-9 match) > #:use-module (srfi srfi-1) > + #:use-module (srfi srfi-71) > #:export (%standard-phases > + with-atomic-json-file-replacement > node-build)) > > -;; Commentary: > -;; > -;; Builder-side code of the standard Node/NPM package install procedure. > -;; > -;; Code: > +(define (with-atomic-json-file-replacement file proc) > + "Like 'with-atomic-file-replacement', but PROC is called with a single > +argument---the result of parsing FILE's contents as json---and should a value > +to be written as json to the replacement FILE." > + (with-atomic-file-replacement file > + (lambda (in out) > + (write-json (proc (read-json in)) out)))) > + > +(define* (assoc-ref* alist key #:optional default) > + "Like assoc-ref, but return DEFAULT instead of #f if no value exists." > + (match (assoc key alist) > + (#f default) > + ((_ . value) value))) > + > +(define* (jsobject-ref obj key #:optional default) > + (match obj > + (('@ . alist) (assoc-ref* alist key default)))) > + > +(define* (alist-pop alist key #:optional (= equal?)) > + "Return two values, the first pair in ALIST with key KEY, and the other > +elements. Equality calls are made as (= KEY ALISTCAR)." > + (define (found? pair) > + (= key (car pair))) > + > + (let ((before after (break found? alist))) > + (if (pair? after) > + (values (car after) (append before (cdr after))) > + (values #f before)))) FWIW, while I don't feel strongly about `let` vs. `define` in general, I find SRFI-71's overloaded `let` less clear than internal definitions and `define-values`, which are supported by core Guile. > + > +(define* (alist-update alist key proc #:optional default (= equal?)) > + "Return an association list like ALIST, but with KEY mapped to the result of > +PROC applied to the first value found under the comparison (= KEY ALISTCAR). > +If no such value exists, use DEFAULT instead. > +Unlike acons, this removes the previous association of KEY (assuming it is > +unique), but the result may still share storage with ALIST." > + (let ((pair rest (alist-pop alist key =))) > + (acons key > + (proc (if (pair? pair) > + (cdr pair) > + default)) > + rest))) > + > +(define (jsobject-update* js . updates) > + "Return a json object like JS, but with all UPDATES applied. Each update > +is a list (KEY PROC [DEFAULT]), so that KEY is mapped to the result of > +PROC applied to the value found for it, or DEFAULT otherwise." > + (match js > + (('@ . alist) > + (let loop ((alist alist) > + (updates updates)) > + (match updates > + (() (cons '@ alist)) > + (((key proc) . updates) > + (loop (alist-update alist key proc #f equal?) updates)) > + (((key proc default) . updates) > + (loop (alist-update alist key proc default equal?) updates))))))) I would prefer (KEY [DEFAULT] PROC). In my experience, DEFAULT is often something simple like #f, and writing it after a multi-line lambda expression is not very pleasant. As a reader, you often want to know what DEFAULT is while reading the body of PROC, whereas putting DEFAULT last can look like a dangling afterthought. Plus, I think indentation tends to work out better with PROC at the end of a clause. The docstring no longer specifies left-to-right evaluation or that the default DEFAULT is #f. (And I still think '(@) is a better default DEFAULT.) I don't especially like all of the explicit quasiquotation of lists in the rest argument. > + > +(define (jsobject-union combine seed . objects) > + "Merge OBJECTS into SEED by applying (COMBINE KEY VAL0 VAL), where VAL0 > +is the value found in the (possibly updated) SEED and VAL is the new value > +found in one of the OBJECTS." > + (match seed > + (('@ . aseed) > + (match objects > + (() seed) > + ((('@ . alists) ...) > + (cons > + '@ > + (fold (lambda (alist aseed) > + (if (null? aseed) alist > + (fold > + (match-lambda* > + (((k . v) aseed) > + (let ((pair tail (alist-pop alist k))) > + (match pair > + (#f (acons k v aseed)) > + ((_ . v0) (acons k (combine k v0 v) aseed)))))) > + aseed > + alist))) > + aseed > + alists))))))) > + > +;; Possibly useful helper functions: > +;; (define (newest key val0 val) val) > +;; (define (unkeyed->keyed proc) (lambda (_key val0 val) (proc val0 val))) I much prefer a keyword argument #:combine, and I still think the key-agnostic case is so much more common that the separation of #:combine/key is useful. -Philip