This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-09-13
Channels
- # announcements (15)
- # babashka (48)
- # beginners (5)
- # biff (4)
- # calva (3)
- # cider (10)
- # clerk (16)
- # clj-kondo (6)
- # cljdoc (20)
- # cljs-dev (13)
- # clojure (117)
- # clojure-argentina (1)
- # clojure-brasil (5)
- # clojure-europe (40)
- # clojure-nl (1)
- # clojure-norway (111)
- # clojure-uk (5)
- # clojurescript (16)
- # cursive (20)
- # datascript (2)
- # datomic (106)
- # etaoin (2)
- # events (3)
- # funcool (1)
- # graphql (1)
- # helix (8)
- # hyperfiddle (36)
- # leiningen (12)
- # matrix (1)
- # nrepl (1)
- # off-topic (61)
- # other-languages (10)
- # polylith (22)
- # practicalli (1)
- # reagent (28)
- # reitit (11)
- # remote-jobs (3)
- # ring (12)
- # shadow-cljs (109)
- # slack-help (6)
- # solo-full-stack (23)
- # squint (7)
- # xtdb (11)
Jeg oppdaget https://clojure-doc.org/ i dag tidlig, som ser ut til Ă„ ha noen nyttige guides.
ja, den er kul! Tror det er @seancorfield som lager den. Mener han har fÄtt noe #clj-together-penger for Ä lage bedre dokumentasjon for Clojure-ting!
ja, den er kul! Tror det er @seancorfield som lager den. Mener han har fÄtt noe #clj-together-penger for Ä lage bedre dokumentasjon for Clojure-ting!
Jeg sitter og leser https://brehaut.net/blog/2011/ring_introduction nÄ, sÄ kom jeg til dette eksempelet (ref. screenshot):
Noe sÄ enkelt er fortsatt ganske mind-blowing for meg. Jeg mÄ innrÞmme at jeg synes det er vanskelig Ä forstÄ hva som skjer i den wrap-uri-check
funksjonen, som tar en funksjon (`handler` parameteret), og pakker den inn i enda en anonym funksjon med map destructuring i parameteret (`[{:keys [uri] :as req}]`).
Selv denne enklere funksjonen synes jeg er litt vanskelig Ä forstÄ:
(defn hello-handler-2 [{:keys [uri]}]
(when (= uri "/hello")
{:body "Hello, World!" :headers {} :status 200}))
Jeg tror det som forvirrer meg er at :keys
i destructuring tilsynelatende ikke blir brukt i bodyen til funksjonen.
NĂ„r ting blir "higher-order" med anonyme funksjoner som blir sendt inn i andre funksjoner, som returnerer funksjoner, og de bruker destructuring, sĂ„ sliter min hjerne med Ă„ henge med i svingene đ Det som er ekstra rart er at jeg forstĂ„r hva som skjer rent teoretisk, men sliter allikevel med Ă„ "lese koden pĂ„ en naturlig mĂ„te," hvis det gir mening.Jeg synes ogsĂ„ at ring-middleware kan bli vanskelig Ă„ lese. Synes ogsĂ„ feilmeldingene blir vanskelig Ă„ lese, sĂŠrlig nĂ„r du sender anonyme funksjoner inni anonyme funksoner! Personlig er jeg veldig glad i HTTP-request og HTTP-response som maps (fra Ring), men jeg blir litt mer skeptisk til superdype middleware-stacks.
Det fÞles veldig rart Ä lese Clojure kode noen ganger. Det er sÄ sykt kompakt, men sÄ tar det meg allikevel mye tid Ä forstÄ. Jeg sitter der og stirrer pÄ skjermen, fÞlger mÞysommelig steg for steg⊠"Aha, der er det en funksjon som tar en funksjon, ja⊠SÄ definerer den en ny funksjon med den funksjonen inni⊠og returnerer en ny funksjon⊠som blir brukt her borte, ja⊠Hvor var jeg igjen? à ja⊠AHA! Det er jo dritkult. Men hva er det egentlig som skjer her?"
Synes det er vanskelig Ă„ si âer dette et god kode jeg bare ikke skjĂžnner?â eller âdette er faktisk en rotete, uintuitiv, overkomplisert mĂ„te Ă„ gjĂžre det pĂ„â noen ganger.
Jeg leser litt pÄ koden til borkdude noen ganger og det er heeelt insane. Noen av funksjonene hans er veldig abstrakte og rett og slett enorme.
Det hender at koden hans gjĂžr veldig mye ogsĂ„! Jeg mĂ„tte sette meg inn i hvordan han jobber pĂ„ babashka (âitselfâ) da jeg skulle jobbe pĂ„ output fra bb print-deps
.
Jeg prĂžvde Ă„ argumentere for borkdude-kodestil https://play.teod.eu/clojure-let/. Argumentet var âdet er OK Ă„ ha en lang let
som gjĂžr mange tingâ. @U04V5VAUN leste over artikkelen for meg, jeg tenkte Ă„ trykke publish, og dele pĂ„ #C8NUSGWG6.
Men han fikk meg til Ä revurdere litt om dette var en god idé, gitt. I mitt tilfelle var funksjonen jeg dro fram som jeg hadde skrevet selv (`olorm-create`) vanskelig/umulig Ä teste. Ved Ä koble ting litt fra hverandre ble det mulig Ä teste!
SĂ„ nĂ„ er jeg ⊠usikker! đ
pleier Ă„ enten ikke destructure, eller eksplisitt mappe med typ {name :name age :age}
osv. Liker Ă„ kunne greppe etter symbolet :age
i koden
Vi pleier «alltid» Ä legge pÄ :as
selvom vi ikke bruker det. Nesten som en type annotasjon
Ser argumentet! Det er jo ikke store forskjellen pÄ
(let [{name :name age :age} person]
(str name " is " age " years old"))
og
(let [{:keys [name age]} person]
(str name " is " age " years old"))
i mengde kode heller. og du fÄr mulighet til Ä endre variabelnavn i kroppen til let
uten Ä rÞre personen som kommer inn.nÄr man ikke har en compiler som enforcer typer sÄ er det jo greit Ä fÄ med navnet ihvertfall, hvis det som sendes inn er et eller annet meningsfylt aggregat i systemet
Jeg er veldig glad i keys destructuring - sĂŠrlig nĂ„r det er vanskelig Ă„ navngi "as" đ
Etter min mening sÄ er funksjonskomposisjon en av de mindre interessante elementene av Clojure-style funksjonell programmering - nettopp fordi det ofte blir opakt og ugjennomtrengelig. Bruk data istedenfor!
Interceptors er vel et eksempel pÄ en mer datadreven versjon av Ring middlewares, tror jeg, men har ikke hatt store nok behov til Ä sette meg ned og teste det.
AngÄende kodestil: Jeg kjenner ikke den konkrete kodestilen det refereres til over, men er generelt skeptisk til bÄde lange funksjoner og let-bindings. let-bindings er et nÞdvendig onde jeg til enhver tid prÞver Ä unngÄ/holde i sjakk.
interessant! For meg fĂžltes det litt som et utpust Ă„ âbare putte alt i en lang letâ nĂ„r jeg ikke kjenner et bedre mĂžnster for Ă„ gjĂžre det jeg skal.
Jeg putter gjerne ting i en lang let
mens jeg finner ut av hva jeg skal gjĂžre, men sĂ„ ender jeg som regel med noe som lett threades. Det er vel min variant av âmake it work, make it prettyâ. Altfor mange stopper etter âmake it workâ
Jeg hater Ä lese kode med mange let-bindings. Det blir utrolig hakkete - du mÄ frem og tilbake, det skaper usikkerhet om hvorvidt ting brukes flere ganger eller ikke, og det krever langt flere navn enn det som er strengt nÞdvendig
Med tanke pÄ at navngivning er noe av det vanskeligste vi gjÞr syns jeg det er et unÞdvendig hinder Ä plassere foran seg selv
jeg tror at jeg har en tendens til Ă„ gi sĂ„nne âmake it workâ bindinger ganske dĂ„rlige navn, eg foo
, bar
eller f*ck
(sÄnn litt avhengig av humÞr) som en pÄminnelse pÄ at de er temporÊre og mÄ vekk.
Jeg bruker let-bindings til ting som skal brukes flere ganger, og ellers forsÞker jeg Ä unngÄ dem ved Ä threade eller trekke ut funksjoner (tommelfingerregel, ingen absolutter osv)
@U04V5VAUN det er en god strategi!
I en nylig frontend PR hadde jeg en type som het TheThing
Äpenbart et sÄ dÄrlig navn at det mÄtte bort.
Jeg jobber nesten alltid ut fra noe thread-bart, og ender av og til med Ă„ flytte noe til en let dersom det trengs flere ganger
> men sÄ ender jeg som regel med noe som lett threades
Jeg tror kanskje âgod clojure-kode er lett Ă„ bruke med ->
eller ->>
â en av de fĂ„ generelle tingene om clojure-koding jeg kan stĂ„ for. I alle fall hvis den transformerer data. (feks som https://www.kodemaker.no/blogg/2020-06-thread-first-and-last/)
Enig, jeg synes threads er mye enklere Ä lese fordi det er sÄ lineÊrt. OgsÄ synes jeg det er enklere Ä definere en gjeng funksjoner som gjÞr spesifikke og avgrensede oppgaver, for deretter sÄ lage "oppskrifter" (threads) hvor jeg kombinerer "ingrediensene" (funksjonene) for Ä prosessere "rÄvarene" (data).

Det er nok den "hakkingen" som @U9MKYDN4Q beskrev tidligere som setter meg ut mest. NÄr jeg stadig mÄ sprette opp og ned i fila, og kanskje til andre filer, og holde pÄ alt dette i hodet samtidig for Ä forstÄ hva som skjer. Jeg mÄ hele tiden "spole tilbake" og lese de samme funksjonene pÄ nytt gjentatte ganger fordi jeg ikke er skarp nok til Ä huske alt jeg mÄ huske for Ä henge med. Det blir jeg utrolig sliten av.
NÄr det gjelder namespaces har jeg begynt Ä tenke at noen av dem kun skal inneholde funksjoner som definerer diskrete "steg" i en "oppskrift." OgsÄ er det andre namespaces som er selve "oppskriftene" hvor disse kombineres. Jeg vet ikke om det er "riktig" mÄte Ä organisere ting pÄ, men det gjÞr det enklere for meg.
nÄ fikk jeg veldig lyst til Ä grave opp en snutt haskell-kode med vill bruk av .
, $
og <$>
helt uten navngitte parametere (point free!). Men jeg skal la vĂŠre Ă„ vĂŠre dust i dag, fordi point free med ->
og ->>
i Clojure betyr noe helt annet enn point free med infix-operatorer i Haskell đ
SĂ„ utelukkende som en spĂžk kan folk gjerne titte innom her og se hvor lett koden er Ă„ lese for deg: https://wiki.haskell.org/Pointfree#Combinator_discoveries
vanskelig Ă„ formulere gode faglige vurderinger utelukkende som regler som skal fĂžlges uten at man tenker selv.
âbruk versjonskontrollâ. Det finnes noen! Ikke sĂ„ mange, da. (Og det er vel sikkert unntak til den ogâŠ)
Jeg er relativt komfortabel med Ä si at nesten alle Clojure-funksjoner som har en let med flere enn 5 bindings kan skrives pÄ en bedre mÄte
liker at du fĂžler at du trenger bĂ„de ârelativt komfortabelâ og ânesten alleâ i den setningen đ
Hadde vi tatt dette over en Ăžl hadde det blitt "all clojure-kode med flere enn 5 letbindings er sĂžppel"
ForĂžvrig, der denne trĂ„den startet. Jeg synes middleware er en god ting for bibliotekforfatter fordi man da gir konsumentene hooks pĂ„ ulike mĂ„ter. Jeg synes middleware i konsumentkode, spesielt middleware-stacks er et helvete. Hos ardoq har vi en middlewarestack pĂ„ hĂžyde med en skyskraper. IMO burde dette vĂŠrt en funksjon som ble lagt inn i som middleware og deretter delegerte til âvanligeâ funksjoner.
Om ikke én, sÄ ihvertfall ikke sÄ mange. Det kan jo vÊre at man Þnsker et litt forskjellig utvalg i prod og dev feks
Inline funksjoner lar seg ikke reloade uten Ä restarte prosessen, sÄ de blir man fort lei av
@U04V5VAUN prĂžver Ă„ skjĂžnne hva du mener her: > IMO burde dette vĂŠrt en funksjon som ble lagt inn i som middleware og deretter delegerte til âvanligeâ funksjoner. Er det noe sĂ„nt du ser for deg? Eller er jeg pĂ„ bĂŠrtur?
Jeg synes forÞvrig at «custom» funksjoner som returnerer funksjoner som regel blir et helvete. For clever lissom.
@U01PE7630AC:
> Jeg tror det som forvirrer meg er at :keys
i destructuring tilsynelatende ikke blir brukt i bodyen til funksjonen.
Jeg vet ikke, men det hÞres pÄ ordbruken din her som at du har misforstÄtt hva :keys
er/gjĂžr?
I destrukturering sier :keys
at âkjĂŠre datamaskin, fra dette map-et henter du ut verdiene fra disse nĂžklene, som er keywords, og bind de til de samme navneneâ.
I den aktuelle funksjonen er det request-map (formodentlig), som har en nĂžkkel :uri
, hvis verdi blir bundet til navnet uri
i funksjonskroppen.
Takk, @U06BEJGKD! Ja, jeg har vel egentlig ikke forstĂ„tt meg helt pĂ„ hvordan https://clojure.org/guides/destructuring#_keyword_arguments fungerer. Det har virket litt "magisk." Men nĂ„r du forklarer det sĂ„ godt med et menneskelig sprĂ„k, blir det straks mye enklere Ă„ forstĂ„! SĂ„ takk for det. > "Any sufficiently advanced technology is indistinguishable from magic." âArthur C. Clarke
> Etter min mening sĂ„ er funksjonskomposisjon en av de mindre interessante elementene av Clojure-style funksjonell programmering [âŠ]
@U07FCNURX, jeg er litt usikker pÄ hva du mente med "funksjonskomposisjon" her. Mente du Ä skrive egne funksjoner som tar funksjoner og returnerer funksjoner, eller mente du bruken av clojure.core
funksjoner som f.eks. comp
, partial
og complement
. Eller kanskje begge deler?
> Jeg synes forÞvrig at «custom» funksjoner som returnerer funksjoner som regel blir et helvete. For clever lissom. Det fÄr holde med dem som er i core.
Samme spÞrsmÄl til deg, @U04V5VAUN. NÄr du sa "dem som er i core," refererte du da til comp
, partial
, complement
, etc.?
Jeg hadde engang en ide om en talk om âThe combinators of Clojureâ, basically en talk om comp
, partial
, complement
, juxt
some-fn
every-pred
og sikkert en gjeng til som jeg ikke husker pÄ sittende rompe. Greia er vel at jeg klarer Ä holde et par tre av dem i hodet (tok meg evigheter Ä forstÄ juxt
, men nÄ husker jeg liksom noen use-caser for det), sÄ jeg trenger liksom ikke 15 sÄnne til som brukes en gang pr stykk i kodebasen.
Det er litt som transpose-sexp
i paredit, jeg har fÄtt til Ä bruke den en gang eller to, men som oftest sÄ skjÞnner jeg ikke at jeg kunne ha brukt den, og nÄr jeg bruker den, sÄ bruker jeg den som regel feil.
Mange av disse kombinatorene er kule hvis du har lyst til Ă„ jobbe point free, men man kan spĂžrre seg om
(def bla (comp (partial foo bar) (partial baz qix)))
gir sÄ veldig mye over:
(defn bla [x]
(->> x
(baz qix)
(foo bar)))
Likeledes kan man spĂžrre seg om
(into {} (map (juxt :foo :bar) xs))
Gir sÄ veldig mye mer mening enn
(set/project xs [:foo :bar])
PĂ„ et litt mer jordnĂŠrt plan, det er litt irriterende at kaffeforbruket mitt pĂ„ 10 dager er 10.2 pakker kaffe đ
clojure.set
har vĂŠrt pĂ„ âjeg skjĂžnner ikke heltâ-lista mi lenge. Jeg leser at det er funksjoner for relasjonell algebra, men klarer liksom ikke ta andre enn set/union
, set/intersection
og set/difference
i bruk.
juxt
liker jeg godt sammen med sort-by
:
Klassiker til glede for nye lesere https://ericnormand.me/mini-guide/clojure-set
Det som er litt rart med clojure.set er at rename-keys
er der, og at Javascript sin implementasjon av Set ikke inneholder union
, intersection
etc.

clojure.set
synes jeg faktisk er litt enklere Ä forstÄ enn en del annet i core, men det er muligens fordi jeg har jobbet mye med "data engineering" og "data science" i SQL, Python, R, samt mange forskjellige GUI-baserte DAG- og ETL/ELT verktÞy (Alteryx, Dataiku, Matillion, SnapLogic, dbt, Airflow, Tableau, Power BI, etc.) hvor det Ä joine sammen mange datakilder via set operations er noe man gjÞre hele tiden hver dag.
juxt
har jeg forsĂžkt Ă„ forstĂ„ flere ganger og forstĂ„r den fortsatt ikke i bruk đ Det samme med de andre "higher-order greiene" i core. Jeg forstĂ„r kanskje hva disse funksjonene gjĂžr isolert sett, men ikke nĂ„r og hvordan jeg bĂžr bruke dem, sĂŠrlig nĂ„r de brukes i kombinasjon. Jeg har sett noen sykt kompakte eksempler som kombinerer disse, og det ser elegant ut, men jeg forstĂ„r ikke hva som skjer đ
Og det er vel noe av det som er problemet. At det blir sÄ veldig kompakt. Noen av oss har jo behov av og til for Ä debugge litt, enten det er med debugger eller med println
. Det er sykt mye enklere Ă„ debugge noe som er skrevet med ->>
enn med tilsvarende greie med comp
og partial
Podkasten #CKKPVDX53 hadde forresten et par gode episoder hvor de fokuserte pÄ juxt
. Da forstod jeg nesten hva den gjorde.
Aha. NÄr @U9MKYDN4Q sa "point free" tidligere forstod jeg ikke av det var https://en.m.wikipedia.org/wiki/Tacit_programming. Kult. Jeg leste om det nÄ og det setter ord pÄ det jeg har tenkt lenge.
Point-free er populĂŠrt i en del andre funksjonelle programmeringssprĂ„k ogsĂ„, feks Elm, Elixir, Haskell og F#. I Elm, Elixir og F# brukes ofte en âpipe-operatorâ der vi i Clojure ville brukt threading-makroer. Sequence-funksjonene i Elm og Haskell tar som i Clojure inn predikat fĂžrst, og sekvens etterpĂ„! Tror Rich var inspirert av Haskell da han satte opp hvordan ->>
funker.
Elm-dokumentasjonen har en fin liten intro: https://elm-lang.org/docs/syntax#operators
Jeg elsker pipe operatoren i R og Elixir. Jeg ser parallellen til threads i Clojure. Det har irritert meg grenselÞst at det ikke finnes noe tilsvarende i C# og Python, som jeg bruker mest pÄ jobb. Man har ogsÄ pipe operator i terminalen (`|`), som jeg bruker flittig.
Jepp! Men⊠Her er et eksempel pÄ hvordan det ser ut med lambdas og "dot-chaining" in the wild:
var labelsDataSimple = (await articleNumberResult
.Map(articleNumber => new Service.Query.GetLabelsForProduct(query.StoreId, articleNumber))
.BindAsync(async result =>
Result<Service.Dto.LabelsDataSimple>.Return(await queryClient.Execute(result))))
.Map(labelsDataSimpleMapper.Map);
Det er ekte C# produksjonskode (som ikke jeg har skrevet, men mĂ„tte fikse en bug inni der). đ
NĂ„ er det riktignok mye annet forbedringspotensiale der (for Ă„ si det mildt), men jeg synes ikke "dot-chaining" er spesielt lesbart.Det gjĂžr det ikke mer lesbart at navnet Map
her bÄde refererer til Map
i .NET Core (tilsvarende map
i clojure.core
) og custom DTO mappers.
@U01PE7630AC Jeg tenker mindre pÄ "egne funksjoner vs clojure.core" og mer pÄ hvordan man bÞr unngÄ Ä pakke inn data i funksjoner som lukker over dem. Jeg vil ha dataene Äpne og fri, tilgjengelige for inspeksjon og manipulasjon, enn gjemt i en tettpakka lÞk av middleware-funksjoner, for eksempel.
Det er klart at clojure.core har mange gode funksjoner som tar andre funksjoner og returnerer funksjoner - disse bruker jeg uten Ä tenke sÄ mye over, i kraft av Ä vÊre byggeklossene i sprÄket, men jeg prÞver Ä unngÄ "funksjonskomposisjon som arbeidsverktÞy" i design av koden min. SÊrlig i de tilfellene hvor funksjoner skjuler data.
Aha, skjÞnner! Takk for forklaringen, @U07FCNURX. Det som fortsatt ikke er helt klart for meg er hvordan data kan brukes istedenfor funksjoner. I mitt enkle hode kan ikke data gjÞre noe av seg selv. Data mÄ vel sendes inn i en funksjon som gjÞr noe med dem. Hvordan unngÄr man da Ä "pakke data inn i funksjoner [som lukker over dem]?" Mener du Ä pakke inn data i in-line/anonyme funksjoner? :thinking_face:
dette er vel kanskje kjernen av FC/IS? SÄnn jeg tolker det handler det om Ä fÄ systemet til Ä generere mest mulig data, sÄnn at fÊrrest mulige ting mÄ utledes ved Ä grave rundt i funksjonshierarkiet
@U01PE7630AC med fare for Ä blÄse i mitt eget horn sÄ viser jeg noen praktiske eksempler i jz-foredraget mitt, feks hvordan vi designet i18n-lÞsningen vÄr rundt data
> Det som fortsatt ikke er helt klart for meg er hvordan data kan brukes istedenfor funksjoner. I mitt enkle hode kan ikke data gjĂžre noe av seg selv. Data mĂ„ vel sendes inn i en funksjon som gjĂžr noe med dem. Jeg vil si at funksjoner som transformerer data er mer âinspiserbartâ / âsebartâ enn funksjoner som transformerer funksjoner. SĂ„ det er ikke â100 % data og ingen funksjonerâ, men litt lettere Ă„ se hva som skjer.
Ja, @U3X7174KS beskriver det godt. Istedenfor Ä sende funksjoner rundt som lukker over data, sÄ send dataene rundt. Dette krever et litt annet tankesett enn Ä sende rundt callbacks og ferdig partial-ede funksjoner, men gir deg langt mer innblikk i hvilke data som flyter. Det kan for eksempel bety Ä ha en event-basert arkitektur istedenfor callbacks.
> Dette krever et litt annet tankesett enn Ă„ sende rundt callbacks og ferdig partial-ede funksjoner, men gir deg langt mer innblikk i hvilke data som flyter. Dette har jeg synes har vĂŠrt skikkelig vanskelig Ă„ lĂŠre med Clojure. Og jeg fĂžler ikke at jeg er i mĂ„l. Jeg ser andre som fĂ„r til enkle lĂžsninger pĂ„ fĂžrste forsĂžk, uten at jeg klarer det selv. âBruk funksjoner og data, ikke gjĂžr det vanskelig!â sier mye â men for Ă„ komme helt til mĂ„lstreken tror jeg man mĂ„ gjĂžre seg opp en del erfaringer selv. Men dette er noe jeg er motivert til Ă„ lĂŠre.
Det er en utvikler som streamer pÄ YouTube som jeg ser pÄ noen ganger. Han koder i alle mulige sprÄk og lÞser problemer i sanntid. Helt rÄ fyr. Skal se om jeg finner kanalen, husker ikke hva den het. Han gjÞr mye kult pÄ fÞrste forsÞk. Men kanskje han har Þvd seg offline, da.
Han har en med Clojure ogsÄ, faktisk! Den har jeg ikke sett fÞr. https://www.youtube.com/watch?v=7fylNa2wZaU