This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-07-16
Channels
- # beginners (110)
- # calva (9)
- # cider (9)
- # clojure (24)
- # clojure-europe (13)
- # clojure-mexico (7)
- # clojure-nl (2)
- # clojure-norway (29)
- # clojure-portugal (1)
- # clojure-uk (6)
- # community-development (2)
- # cursive (10)
- # data-science (8)
- # datalevin (2)
- # fulcro (7)
- # graalvm (1)
- # humbleui (4)
- # hyperfiddle (38)
- # instaparse (2)
- # integrant (4)
- # malli (23)
- # missionary (2)
- # overtone (1)
- # polylith (2)
- # reitit (2)
- # releases (1)
- # shadow-cljs (24)
- # testing (6)
- # uncomplicate (4)
Dagens nøtt:
1. hva evaluerer ((fn [{:as x}] x) {})
til?
2. hva evaluerer ((fn [{:as x}] x) [])
til?
3. hva evaluerer ((fn [{:as x}] x) ())
til?
:thinking_face:
Jeg gjetter på at den siste oppfører seg annerledes, siden den ikke er assosiativ? Key-destrukturering fungerer vel bare på assosiative strukturer, så da blir kanskje lista konvertert på noe vis?
Nysgjerrigheten tok meg, jeg måtte finne laptopen og prøve 😄 Fra observert oppførsel så tipper jeg at ikke-assosiative datastrukturer går gjennom
Jeg tok feil. Det funker med partall elementer, men jeg er neigu ikke sikker på akkurat hvordan det foregår.(into {} ,,,)
så det funker med lister av partall antall elementer.
Det er egentlig ikke en god nøtt, siden jeg vet ikke om noen måte å kunne svare på det uten å bare vite veldig mye om clojure 😅 Jeg antok at denne funksjonen ville oppføre seg som identitetsfunksjonen (den gjør det for blant annet tall og strenger), som jeg lærte ikke stemte på ganske frustrerende vis 😇
Ja, men det syns jeg egentlig er mest sjarmerende. At :as x
gir en x
som er bundet til noe annet enn det du puttet inn i funksjonen derimot…
Det syns jeg ikke er overraskende. Du har da allerede bedt om en assosiativ struktur. [:as x]
vil antageligvis gi deg en seq?
Slik jeg leser [guiden](https://clojure.org/guides/destructuring) til destructuring, så virker det som at :as
kun skal opprette en binding til verdien som kommer inn, og da er det overraskende at den gjør noe mer. Så langt er lister det eneste moteksemplene jeg har funnet.
(defn bad-identity [{:as x}] x)
(bad-identity '()) ; => {}
(bad-identity '(1)) ; => 1
(bad-identity '(1 2)) ; => {1 2}
(bad-identity '(1 2 3)) ; => Execution error
Min nåværende hypotese er at bad-identity
er identitetsfunksjonen på alt annet enn lister, som til gjengjeld har helt bananas semantikk 🙃Ja, det var den som brant meg også! Jeg gjorde en clojure.walk/postwalk
og brukte destructuring, og transformerte elementene dersom en verdien for en bestemt nøkkel oppfylte et resultat. Da ble ett-elementslistene avkledd lista si 😬
Mer spesifikt, gjorde jeg dette:
(defn pandocir-transform [pandoc filters]
(walk/postwalk
(fn [{:pandocir/keys [type] :as node}]
(let [filter (filters type)]
(cond-> node filter filter)))
pandoc))
men nå har jeg lært 🙂Hypotesen om «identitetsfunksjonen for alt som ikke er lister» stiller sterkt i møte med clojure.test.check
: https://gist.github.com/larstvei/ec36179e1ab772f000e7b10ec825d230
Dette er kanskje ikke nytt for mange, men Clojure implementerer destrukturering som en macro, og har lenge hatt støtte for å destruktere seqs assosiativt, som lar deg lage og kalle på funskjoner slik:
(defn foo [& {:keys [a b] :as args}]
[a b args])
(foo {:a 1 :b 2}) ;; [1 2 {:a 1, :b 2}]
(foo :a 1 :b 2) ;; [1 2 {:a 1, :b 2}]
Hvis du lurer på hvordan den skiller mellom ()
og []
så tror jeg svaret ligger her https://github.com/clojure/clojure/blob/8c8e4f6ef21f3d0f59deb60cdc7e1b584f596d59/src/clj/clojure/core.clj#L4472Ja. Det blir også ganske klart ved å bruke macroexpand
:
(macroexpand '(let [{:as x} m] x))
;; =>
(let*
[map__12858
m
map__12858
(if
(clojure.core/seq? map__12858)
(if
(clojure.core/next map__12858)
(clojure.lang.PersistentArrayMap/createAsIfByAssoc
(clojure.core/to-array map__12858))
(if
(clojure.core/seq map__12858)
(clojure.core/first map__12858)
clojure.lang.PersistentArrayMap/EMPTY))
map__12858)
x
map__12858]
x)
For meg virker det som at bindingen x map__12858
egentlig er en bug. Jeg skjønner at den forsøker å gjøre sekvenser den får inn til en assosiativ struktur, men jeg ser ikke hvorfor det som bindes av :as
skal bli påvirket av den konverteringen...Altså at i eksempelet ditt så ville du forventet at (= x m)
holdt enten om m
var et hash-map eller en seq?
Jeg mener ikke at det nødvendigvis er fornuftig å bruke map destructuring på noe som ikke er et map (eller en assosiativ struktur). Men enn så lenge man får lov (ingen unntak kastes), og den oppfører seg som identitetsfunksjonen for all clojure-data med unntak av lister, så synes jeg det unntaket lukter som en bug. Fra macroexpansionen ser det ut som at x
bør bindes til map__12858
før den gjøres om:)
For å ta eksempelet fra https://clojure.org/guides/destructuring:
(defn configure [val & {:keys [debug verbose]
:or {debug false, verbose false}
:as opts}]
(println "val =" val " debug =" debug " verbose =" verbose " opts =" opts))
(configure 5 :debug true) ;; this is supported
Grunnen til at dette støttes er blant annet å gjøre det lettere å kalle på funskjoner i REPL, eller i kommandolinje uten å måtte escape {
og }
. Og gitt dette, så tror jeg det er å foretrekke at :as x
binder x
til et hash-map, heller enn at configure
må sjekke om opts
var et hash-map eller ikke.Å gi {:as x}
noe annet enn hash-map eller seq er likt som å si (clojure.set/difference #{2} [1])
. Det er mye som "er lov" (kaster ikke) i Clojure selv om det ikke er korrekt.
Jeg ser hvordan [& {:as x}]
gjør det vanskeligere å kunne si at x
alltid skal være bundet til det som kom inn. Slik jeg har tolket destructuring av maps (kun ett enkelt map) er at {:keys [k] :as x}
binder k
til en non-`nil` verdi, hvis x
er en ting (ikke nødvendigvis en map) som har en nøkkel k
. Og så har jeg antatt at x
er inputet (ubesudlet). Jeg synes ikke det kommer veldig godt frem fra dokumentasjonen at det ikke er tilfellet.