From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: excalamus--- via Users list for the GNU Emacs text editor Newsgroups: gmane.emacs.help Subject: alist keys: strings or symbols Date: Sun, 19 Jul 2020 18:23:52 +0200 (CEST) Message-ID: Reply-To: excalamus@tutanota.com Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="32502"; mail-complaints-to="usenet@ciao.gmane.io" To: help-gnu-emacs@gnu.org Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Sun Jul 19 18:29:53 2020 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1jxCCG-0008Ju-Ko for geh-help-gnu-emacs@m.gmane-mx.org; Sun, 19 Jul 2020 18:29:52 +0200 Original-Received: from localhost ([::1]:43002 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jxCCF-0008JC-KD for geh-help-gnu-emacs@m.gmane-mx.org; Sun, 19 Jul 2020 12:29:51 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:58334) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jxC6Z-0007eS-Cq for help-gnu-emacs@gnu.org; Sun, 19 Jul 2020 12:23:59 -0400 Original-Received: from w1.tutanota.de ([81.3.6.162]:37910) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jxC6W-0002ry-8D for help-gnu-emacs@gnu.org; Sun, 19 Jul 2020 12:23:59 -0400 Original-Received: from w3.tutanota.de (unknown [192.168.1.164]) by w1.tutanota.de (Postfix) with ESMTP id 604D0FA03B1 for ; Sun, 19 Jul 2020 16:23:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1595175832; s=s1; d=tutanota.com; h=From:From:To:To:Subject:Subject:Content-Description:Content-ID:Content-Type:Content-Type:Content-Transfer-Encoding:Content-Transfer-Encoding:Cc:Date:Date:In-Reply-To:MIME-Version:MIME-Version:Message-ID:Message-ID:Reply-To:References:Sender; bh=nG4v14gXT3+JB0cU2gDiC9t4QMU2tKSenJXfDhOTBQc=; b=KZOT1rf2rhbe4cpFeuxHblEiHOZeMZTeIutNSiknYrwSqjEuCYf1ha4xzkCphIH/ jbf2rQQJeg/Gd0ffneD2p/O11vH6L11I2fBRvt8gmpCe4ncSK0dWd7Q9BwibV9uraGz FaVk2Iny3Fd713uXm0OMn6ZRH1V/xEV8rL/H11KDb7mNAGcG3N40Kn8MD2x/bAFwow8 f7aO4HuMZFCB6AkK4hL+ls2M8vM6Tdoe9iRWnqUMlLfK9GcvMHmeIlc0xNHN8ouukXU waXyUHPKlkb2T/EfhnzuosAv+l6E7wQm+3SWQ5jOiuv0Dd07/CFyWbwZnJDxW13BQwm abkhpN0Vlg== Received-SPF: pass client-ip=81.3.6.162; envelope-from=excalamus@tutanota.com; helo=w1.tutanota.de X-detected-operating-system: by eggs.gnu.org: First seen = 2020/07/19 12:23:52 X-ACL-Warn: Detected OS = Linux 2.2.x-3.x [generic] X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_MSPIKE_H4=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Sun, 19 Jul 2020 12:28:54 -0400 X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "help-gnu-emacs" Xref: news.gmane.io gmane.emacs.help:123563 Archived-At: Some questions about alists: - Is it a better practice to convert string keys to symbols?=C2=A0 Is =C2=A0 =3Dintern=3D best for this?=C2=A0 What about handling illegal symbol= names? - If a symbol is used as a key and that symbol is already in use =C2=A0 elsewhere, is there potential for conflict with the existing symbol? I have an alist created from parsing meta data from a file.=C2=A0 The file looks like: #+begin_src emacs-lisp :results verbatim :session exc (defvar exc-post-meta-data =C2=A0 (concat =C2=A0=C2=A0 "#+TITLE: Test post\n" =C2=A0=C2=A0 "#+AUTHOR: Excalamus\n" =C2=A0=C2=A0 "#+DATE: 2020-07-17\n" =C2=A0=C2=A0 "#+TAGS: blogging tests\n" =C2=A0=C2=A0 "\n") =C2=A0 "Sample post meta information.") (defvar exc-post-content =C2=A0 (concat =C2=A0=C2=A0 "* Header\n" =C2=A0=C2=A0 "** Subheader\n" =C2=A0=C2=A0 "Hello, world!\n\n" =C2=A0=C2=A0 "#+begin_src python\n" =C2=A0=C2=A0 "=C2=A0=C2=A0=C2=A0 print('Goodbye, cruel world...')\n" =C2=A0=C2=A0 "#+end_src\n") =C2=A0 "Sample post file without meta information.") (defvar exc-post =C2=A0 (concat =C2=A0=C2=A0 exc-post-meta-data =C2=A0=C2=A0 exc-post-content) =C2=A0 "Sample post file.") (message "%s" exc-post) #+end_src #+RESULTS: #+begin_example "#+TITLE: Test post ,#+AUTHOR: Excalamus ,#+DATE: 2020-07-17 ,#+TAGS: blogging tests ,* Header ,** Subheader Hello, world! ,#+begin_src python =C2=A0=C2=A0=C2=A0 print('Goodbye, cruel world...') ,#+end_src " #+end_example The meta data is parsed into an alist: #+begin_src emacs-lisp :results verbatim :session exc (defun exc-parse-org-meta-data (data) =C2=A0 "Parse Org formatted meta DATA into an alist. Keywords are the '#+' options given within an Org file.=C2=A0 These are things like TITLE, DATE, and FILETAGS.=C2=A0 Keywords are case-sensitive!.=C2=A0 Values are whatever remains on that line." =C2=A0 (with-temp-buffer =C2=A0=C2=A0=C2=A0 (insert data) =C2=A0=C2=A0=C2=A0 (org-element-map (org-element-parse-buffer 'element) 'ke= yword =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (lambda (x) (cons (org-element-property :key= x) =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (org-elemen= t-property :value x)))))) (setq exc-alist (exc-parse-org-meta-data exc-post)) exc-alist #+end_src #+RESULTS: : (("TITLE" . "Test post") ("AUTHOR" . "Excalamus") ("DATE" . "2020-07-17")= ("TAGS" . "blogging tests")) Notice that the keys are strings.=C2=A0 This means that they require an equality predicate like =3D'string-equal=3D to retrieve unless I use =3Dassoc=3D and =3Dcdr=3D: #+begin_src emacs-lisp :results verbatim :session exc (alist-get "TITLE" exc-alist) #+end_src #+RESULTS: : nil #+begin_src emacs-lisp :results verbatim :session exc (cdr (assoc "TITLE" exc-alist)) #+end_src #+RESULTS: : "Test post" I can use =3Dassoc/cdr=3D well enough.=C2=A0 The bother starts when I need a default.=C2=A0 It looks like =3Dalist-get=3D is what I need. #+begin_src emacs-lisp :results verbatim :session exc (alist-get "TYPE" exc-alist 'post nil 'string-equal) #+end_src #+RESULTS: : post This works, but now the code is getting messy. There are two forms of lookup: the verbose =3Dalist-get=3D and the brute force =3Dassoc/cdr=3D.=C2= =A0 One requires =3D'string-equal=3D, the other does not.=C2=A0 If I forget the predicate, the lookup will fail silently. I could create a wrapper for =3Dalist-get=3D which uses =3Dstring-equal=3D: #+begin_src emacs-lisp :results none :session exc (defun exc-alist-get (key alist &optional default remove) =C2=A0 "Get value associated with KEY in ALIST using `string-equal'. See `alist-get' for explanation of DEFAULT and REMOVE." =C2=A0 (alist-get key alist default remove 'string-equal)) #+end_src Now my calls are uniform and a bit more safe: #+begin_src emacs-lisp :results verbatim :session exc (exc-alist-get "TITLE" exc-alist) #+end_src #+RESULTS: : "Test post" #+begin_src emacs-lisp :results verbatim :session exc (exc-alist-get "TYPE" exc-alist 'post) #+end_src #+RESULTS: : post This works, but seems like a smell.=C2=A0 All these problems go back to strings as keys.=C2=A0 Maybe there's a better way? I could convert the keys to symbols using =3Dintern=3D.=C2=A0=20 #+begin_src emacs-lisp :results verbatim :session exc (defun exc-parse-org-meta-data-intern (data) =C2=A0 "Parse Org formatted meta DATA into an alist. Keywords are the '#+' options given within an Org file.=C2=A0 These are things like TITLE, DATE, and FILETAGS.=C2=A0 Keywords are case-sensitive!.=C2=A0 Values are whatever remains on that line." =C2=A0 (with-temp-buffer =C2=A0=C2=A0=C2=A0 (insert data) =C2=A0=C2=A0=C2=A0 (org-element-map (org-element-parse-buffer 'element) 'ke= yword =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (lambda (x) (cons (intern (org-element-prope= rty :key x)) =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (org-elemen= t-property :value x)))))) (setq exc-alist-i (exc-parse-org-meta-data-intern exc-post)) exc-alist-i #+end_src #+RESULTS: : ((TITLE . "Test post") (AUTHOR . "Excalamus") (DATE . "2020-07-17") (TAGS= . "blogging tests")) This has several apparent problems. As I understand it, this would pollute the global obarray. Is that a real concern?=C2=A0 I know the symbol is only being used as a lookup; the variable, function, and properties shouldn't change.=C2=A0 Regardless, I don't want my package to conflict with (i.e. overwrite) a person's environment unknowingly. The string may also have characters illegal for use as a symbol.=C2=A0=20 Here's what happens with illegal symbol characters in the string. #+begin_src emacs-lisp :results verbatim :session exc (setq exc-bad-meta-data =C2=A0 (concat =C2=A0=C2=A0 "#+THE TITLE: Test post\n" =C2=A0=C2=A0 "#+AUTHOR: Excalamus\n" =C2=A0=C2=A0 "#+DATE: 2020-07-17\n" =C2=A0=C2=A0 "#+POST TAGS: blogging tests\n" =C2=A0=C2=A0 "\n")) (setq exc-alist-i-bad (exc-parse-org-meta-data-intern exc-bad-meta-data)) exc-alist-i-bad #+end_src #+RESULTS: : ((AUTHOR . "Excalamus") (DATE . "2020-07-17")) How are situations like these best handled?