Fork me on GitHub
Nazar Trut00:09:02

(defn loop-over-sets
  [expr kb]

  (filter #(and (= (first %) 'if) (= (second %) expr)) kb)

Im getting a dont know how to create from ISEQ error when my expr = 'a and when my kb = '(if a b)

Nazar Trut00:09:07

does anyone know why?

Nazar Trut00:09:33

this works when i have kb = '((if a b)(if b c))


Having just '(if a b) as kb doesn't sound right based on what you've described: that's a sequence of just if, a, and b. I think you want kb to be '((if a b)) i.e., a sequence of expressions.


(and the reason for the error is that filter will run that predicate over if, a, and b and the predicate calls first and second which aren't valid on symbols (but are valid on sequences).

Nazar Trut00:09:50

hmmm is there a way to change '(if a b) to '((if a b)) in code?

João Galrito00:09:18

I think you should probably rethink how you're calling that function

João Galrito00:09:39

are you trying to loop over a list of expressions and return the results for each?

Nazar Trut00:09:41

its because kb can be both

Nazar Trut00:09:35

yeah so, What i want to happen is, if kb is ((if a b)(if b c)) and expr is 'a. It would look for "if a" and then it would derive b. And if kb is just '(if a b), then it would still derive b

Nazar Trut00:09:55

I think if i do if statement saying if first = 'if, then derive the 2nd nth else use that filer function

Nazar Trut00:09:01

it would work

João Galrito00:09:21

don't know if there's a better way to do it, but you can (if (list? (first kb)) kb (list kb)) I guess


It's not good practice to have a data structure that can be different shapes like that. kb should always be a list of expressions, even if it has just one expression in it.


(that whole series of do's and don'ts is worth reading)

Nazar Trut00:09:15

I understand what im doing wrong but how do i make sure that kb is '((if a b)) and not '(if a b) when being passed in


You should solve that problem at the source of where kb is being created -- and ensure it is always a sequence of expressions and never a single expression.

Nazar Trut00:09:12

is there a way to change kb to '((if a b))


In other words, your "loop over sets" (terrible name 🙂 ) should always just assume it is passed a sequence of expressions.


It should not be trying to accommodate someone calling it with invalid data.


How/where is kb created? That's where you should fix the problem.

Nazar Trut00:09:48

(defn fwd-infer
  [prob kb]

    (= (first prob) 'and) (fwd-infer (first (and-elimination prob)) #(and-elimination prob))
    (= (first prob) 'not) (fwd-infer (elmination-not prob) #(elmination-not prob))
    (= (first prob) 'if) (fwd-infer (modus-ponens (nth prob 1) kb) #(modus-ponens (nth prob 1) kb))

This is where kb is created

Nazar Trut00:09:17

It is just passed through my infer function

João Galrito00:09:50

so it's the and case that might return just one element

João Galrito00:09:11

but you're not creating kb there

João Galrito00:09:15

you're just passing it

Nazar Trut00:09:52

For my homework, we just simply use the REPL to pass our data in

João Galrito00:09:23

so it is a requirement that it accepts both '(if a b) and '((if a b) (if c b))?

João Galrito00:09:40

i'd say just transform the data when you read it

João Galrito00:09:09

if the first form is not a list, wrap everything in a list

Nazar Trut00:09:20

There is maybe a better way to do this but this is the only way i could think of doing it

Nazar Trut00:09:25

Ok ill try that

João Galrito00:09:36

or you add an 'or and do '(if (or a c) b) 😛

Nazar Trut00:09:01

how would i wrap everything in a list

João Galrito00:09:20

i gave you the code earlier

misto quente01:09:46

hello, I have a lein project i’m trying to add shadow-cljs to, first time using it (I’m working from the book Web Dev with Clojure) I have, src/clj and src/cljs folders each with a guestbook.core namespace. Could this be causing an issue? I am getting this error trying to start up shadow-cljs, I’ve looked over my project.clj and shadow-cljs.edn a few times and nothing’s jumping out at me as incorrect…

[:app] Configuring build.
[:app] Compiling ...
[:app] Build failure:
The required namespace "guestbook.core" is not available, it was required by "guestbook/app.cljs".
"guestbook/core.clj" was found on the classpath. Should this be a .cljs file?


Your namespace is requiring guestbook.core -- but is ClojureScript and guestbook.core is Clojure, based on the extensions.


If both src/clj and src/cljs are on your classpath, you can't have the same named file in two places -- only one of them will be found.


So it's finding src/clj/guestbook/core.clj

👍 1

ClojureScript apps do tend to have .clj files in them for macros (since ClojureScript relies on Clojure for macro definition and expansion, prior to compilation -- to JavaScript).

misto quente01:09:51

thanks. just tried renaming the cljs file and ns, but it’s still not finding it..must be something else im not seeing


Are you getting that same error, or a different one?


The error above is that it is finding guestbook.core but it's expecting a .cljc or .cljs extension on it.

misto quente01:09:44

oof…i was missing :source-paths [..., "src/cljs"] in my project.clj … build completed now with the original filenames maintained, there’s a core.clj and a core.cljs using the same ns in the project. Not sure if i’ll run into issues down the line…

misto quente02:09:31

i was using lein cljsbuild previously, and removed that config entirely which had its own source path prop


Glad you got it working, and thank you for reporting back with the solution!

João Galrito01:09:21

what is the best way to redefine a binding in an outer scope, for ex:

(let [x 1]
   (do-something-to-x-to-make-it 2)
   (+ x 1))

João Galrito01:09:55

what could be the approach? I'm traversing a tree and need to maintain some state


Local bindings (established via function application or let) are immutable


Look at tree traversals in functional languages


General you use recursive function calls


That style is what works in clojure, but sometimes you use loop/recur in place of recursive function calls, but it will have the same shape as a recursive function call style

João Galrito01:09:44

i'm traversing the tree alright

João Galrito01:09:11

the problem is i want to pass some state between sibling branches

João Galrito01:09:47

I know about atom, thought it was just used for concurrency stuff


its a threadsafe mutable container.


You can return information from traversals and pass it to sibling traversals

João Galrito01:09:11

yeah, that's what i was thinking

João Galrito01:09:04

in this case i'm traversing the tree using a multimethod

João Galrito01:09:15

so i should wrap it in another function that deals with the state

João Galrito01:09:21

and call the multimethod inside that

hiredman01:09:46 is a rather complicated function (a macro expander for another programming language) that walks a tree (expressions in the language) and passes information down and up and around

João Galrito01:09:40

@hiredman that's more or less what I'm doing here


the "trick" to make sure your state goes into the siblings is to use a reduce to keep accumulating state between the siblings.

João Galrito01:09:51

ok, my issue is that now I have to deal with state at every node when the majority of them don't care about it

João Galrito02:09:15

I think atom is what I want here

Nazar Trut03:09:52

(defn loop-over-sets
  [expr kb]

  (filter #(and (= (first %) 'if) (= (second %) expr)) kb)
This gives me the correct output with (if a b)(if b c) as kb and b as expr but when i have kb = (if a b) a (if b c) and my expr = b, I want to get (if b c) but im getting a "Don't know how to create ISeq from: clojure.lang.Symbol"

Nazar Trut03:09:42

@joao.galrito Do you know if i could change this function so it can read both ((if a b)(if b c)) AND '((if a b) a (if b c))

João Galrito03:09:32

what do you want to do with the 'a in the middle

Nazar Trut03:09:55

because this function only looks for ifs

Nazar Trut03:09:31

it should look for ifs

João Galrito03:09:16

then you need to add another condition to your filter predicate

João Galrito03:09:31

to check if what you're reading is a list

João Galrito03:09:18

because right now you're trying to call (first 'a)

Nazar Trut03:09:25

I figured it out


sequential? is probably the predicate you want here.

Nazar Trut03:09:37

nvm i havent got it

Nazar Trut03:09:05

haha this is hard

Nazar Trut03:09:13

Problems only lead to more problems

João Galrito03:09:14

@seancorfield any particular reason? If just working with lists


(filter #(and (sequential? %) (= (first %) 'if) (= (second %) expr)) kb)

João Galrito03:09:36

(instead of list? for example)



user=> (list? ())
user=> (list? (range 5))
user=> (list? (map inc (range 5)))
user=> (sequential? ())
user=> (sequential? (range 5))
user=> (sequential? (map inc (range 5)))

João Galrito03:09:23

if at any point it turns into a lazy list it breaks


list? is very specific and if Nazar is building the kb programmatically, he might get sequences that are not lists.

João Galrito03:09:33

he said kb is coming from the REPL


But he's processing it with fwd-infer for the reductions?

João Galrito03:09:19

but even then just using filters and maps can result in a lazyseq


Yup. Test for the abstraction, not the concrete type.


I am using following code to create an empty file.

(defn create-seed [name]
  (with-open [wtr (io/writer (get-seed-file-name name))]
    (.write wtr "")))
Is there a shorter version?


I was hopping to have something as concise as touch afile.txt


Thanks I will try it.


(spit (get-seed-file-name name) "")


It's a built-in version of the code you posted:

user=> (source spit)
(defn spit
  "Opposite of slurp.  Opens f with writer, writes content, then
  closes f. Options passed to ."
  {:added "1.2"}
  [f content & options]
  (with-open [^ w (apply jio/writer f options)]
    (.write w (str content))))


Now I see the source, I guess you could even do (spit (get-seed-file-name name) nil) which might be more explicit?


Since (str nil) is ""


Thanks @seancorfield it worked like a charm


I wish we had a karma coin, so that I could give some to you. 🙂


(.createNewFile (io/file "bob"))


read javadocs for File and there's something that could help right away


Also, what do you want to happen if the file already exists?


Just error out.


(spit ... nil) will overwrite it with an empty string; (spit ... nil :append true) would append an empty string to an existing file (i.e., leave it unchanged apart from the mod date/time).


I suspect .createNewFile would error out if it already exists


Ah, no, it would just return false


(and it returns true if it successfully creates the file)


io/file is very confusing. I am using following code to list the files in a directory.

(->> (io/file "resources/seeds")
As @U11BV7MTK pointed out (.createNewFile (io/file "bob")) creates a new file. What exactly is (io/file 'path)' ?


user=> (.createNewFile ( "/tmp/foo"))
true ; file did not exist, this created it
user=> (.createNewFile ( "/tmp/foo"))
false ; file existed so it was not touched


io/file creates a .File object so you'll have to read the Java API docs for that.


But it's basically an abstraction for a file.


So it can be a file that doesn't exist, a directory, a file that does exist, or any implementation of something that can behave like a file.


hmm got it


For my use case I will use (.createNewFile


Java has dramatically improved its support for file ops in more recent versions, so now there's a java.nio.* package hierarchy with abstractions for paths and files and entire file systems 🙂


Clojure pretty much just delegates to Java interop for file stuff.


Yeah I can see that. I think clojure's biggest weakness is its error messages.


Most of the time the relevant files are buried somewhere in the middle of stack trace.


@suren You get used to reading (Java) stack traces but they can be daunting at first.


Yeah too much noise in the stack traces.


Not sure if this is the best section to ask but for a serious web project would you go with ring/reitit or pedestal?

Ilari Tuominen08:09:05

@U06FM9QN9 both are definitely for serious projects. I would go with ring/reitit but it’s because I’m more familiar with it and my colleagues are more inclined to it. I’d still go through both documentations and decide which is more to your liking. Also reitit comes with swagger support out of the box but that functionality is easily available for pedestal as well so they’re not too far apart. I’m still very much a clojure beginner so somebody with actual production experience with both should chime in.

Ilari Tuominen08:09:40

I would check the reitit faq for some explanation of the differences


Yeah. I’ve touched on both but I believe I’m going to focus on reitit. It’s a little friendlier to beginners while at the same time being more than performant.


Assuming a function has too take two arguments, but you want one of them to just not do anything, is just doing () an ok option? I only want to log something based on a if-dev macro which I am unable to modify


actually I think nil is better now that I think of it


Are there any clever clojure function to convert booleans to 0/1?


like if? (if x 1 0)


Yea, but the other way around 🙂


Oh nvm 😄


({true 1 false 0} x)

👍 1

Maybe you want a default result:

(defn judge [x] 
  (get {true 1 false 0} x -1))


maps have the same interface as get) ({true 1 false 0} x -1) also will give you default value


Thank you @U04V4KLKC, I have read from somewhere that only get can append a default value.

michele mendel12:09:53

Why are

(apply * []) equal 1
(apply + []) equal 0
? Has it anything to do with 1 and 0 being the identities of monoids with operators * and +?

João Galrito14:09:53

does doseq not realize a lazy seq?


sounds like doall ?

João Galrito14:09:27

so doall / dorun + map for iterating over a seq for side effects?

João Galrito14:09:48

thought that was what doseq was for

João Galrito14:09:38

I see there's also run!


doseq does iterate over lazy sequences, but it doesn't return anything. Use it when you need the side effects, but don't care about the values!

João Galrito14:09:46

I tried doseq but nothing happened


What did you try? Here's an example:

user=> (doseq [x (range 5)] (prn x))

João Galrito14:09:24

(doseq [operations chunks]
        (str (env :es-host) "/_bulk")
        {:body (str (string/join \newline operations) \newline)
         :content-type :json})
      (println "Inserted " (count operations)))

João Galrito14:09:42

where chunks is

(->> documents
                    (map #(str (json/write-str {:index {:_index collection-name
                                                        :_id (get % id-key)}})
                               (json/write-str %)))
                    (partition-all max-chunk-size))


I don't see anything that's wrong. Do you observe your (println "Inserted " (count operations)) output?


> anyway in my case I can take advantage of parallelism so ended up using `pmap` and `dorun` Looks like you worked it out!


if you want to iterate for side effects, I would recommend run!

João Galrito14:09:04

oh, I think doseq realizes the sequence before executing the body

João Galrito14:09:38

anyway in my case I can take advantage of parallelism so ended up using pmap and dorun

Clark Urzo16:09:47

I'm playing with a deps.edn project and I'm having some difficulty starting up my REPL properly

Clark Urzo16:09:56

So, here's my deps.edn file:

{:paths ["src"]
 :deps {org.clojure/clojure                  {:mvn/version "1.10.1"}
        com.datomic/datomic-free             {:mvn/version "0.9.5697"}
        com.datomic/client-pro               {:mvn/version "0.9.63"}
        org.postgresql/postgresql            {:mvn/version "9.3-1102-jdbc41"}
        http-kit                             {:mvn/version "2.4.0"}
        metosin/reitit                       {:mvn/version "0.5.5"}}
 :aliases {:server      {:main-opts   ["-m" "grok.core"]}
           :dev         {:extra-paths ["config/dev" "env/dev"]}
           :test        {:extra-paths ["test" "config/test"]
                         :extra-deps  {lambdaisland/kaocha {:mvn/version "0.0-529"}
                                       lambdaisland/kaocha-cloverage {:mvn/version "1.0.63"}}
                         :main-opts   ["-m" "kaocha.runner"]}
           :socket-repl {:jvm-opts ["-Dclojure.server.repl={:port,50505,:accept,clojure.core.server/repl}"]}}}

Clark Urzo16:09:43

When I do clj -A:dev:socket-repl, I get a REPL in the namespace user

Clark Urzo16:09:32

However, I can't do (ns and have any definitions in said namespace be loaded in the REPL


that declares a namespace. you probably want (require '

Clark Urzo16:09:06

I tried that but it's unable to resolve any of the definitions


can you paste what you've tried?

Clark Urzo16:09:11

Lambda Island ( says the REPL that appears when you do this isn't the socket REPL per se, and you need to connect to it via netcat (`nc localhost 50505`)

Clark Urzo16:09:22

user=> (require 'grok.db.core)
user=> database-uri
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: database-uri in this context


'(require '[grok.db.core :as grok]) (grok/database-uri)`


or you can read about in-ns from the link above to know how to get into a namespace

Clark Urzo16:09:02

Mmm, still not resolving. I also tried doing (in-ns 'grok.db.core) to no avail

Clark Urzo16:09:30

It's weird, because there was one time it worked and I could access everything from the REPL

Clark Urzo16:09:13

When I reconnected via netcat, I suddenly had the same errors appearing again


if you've already run (ns grok.db.core) you've created a new empty namespace. restart your repl and then do (require '[grok.db.core]) (in-ns grok-db.core)

Clark Urzo02:09:18

I think I got it now. I was spoiled by lein in that I didn't have to require namespaces there before switching to them: I can just do it directly with ns

Clark Urzo02:09:36

Your solution definitely works. Thanks!

Clark Urzo02:09:14

One thing that bothers me though is why in-ns works when you pass in a nonexistent namespace


Per the docstring: "Sets ns to the namespace named by the symbol, creating it if needed."


So it will create a completely empty namespace if it doesn't already exist. It won't have any clojure.core functions referred in.

Clark Urzo02:09:46

Is there a usecase for that? I'd hate to be derailed by an errant typo

Clark Urzo02:09:07

Ahh, hmm so how does it differ from ns then?


ns does a whole bunch of stuff: creates the ns if needed, refer in clojure.core and a bunch of other stuff, requires specified namespaces, imports specified classes, etc.


in-ns is very low-level.

Clark Urzo02:09:41

Wait, you meant *ns* right?

Clark Urzo02:09:45

I see, I see


I mean the ns macro. Not *ns*. The latter is a dynamic Var.

user=> (in-ns '
#object[clojure.lang.Namespace 0xdf5f5c0 ""]> (ns-publics *ns*)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: ns-publics in this context> (clojure.core/ns-publics *ns*)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: *ns* in this context> (clojure.core/ns-publics clojure.core/*ns*)
{}> (clojure.core/ns-refers clojure.core/*ns*)
{}> (clojure.core/ns-aliases clojure.core/*ns*)

Clark Urzo02:09:12

BTW I saw your post earlier this year on whether or not switching to deps.edn is a good idea and it's the main reason I'm trying to do it now. Thanks!


See how I have to specify clojure.core/ in front of various symbol names? Normally -- when you use the ns macro -- those are automatically referred in.

👍 1
Clark Urzo02:09:33

I mean in your first reply, since *ns* appears as ns


Oh, this? Per the docstring: "Sets `*ns*` to the namespace named by the symbol, creating it if needed." Yeah, I just pasted in the docstring and forgot it would auto-format that!

clojure-spin 1
Clark Urzo02:09:08

Just checked ClojureDocs, I should probably check it more often. In any case, thanks for the detailed responses! Really starting to get the hang of this thing.


Don't forget you can use doc and source right there in your REPL.

Clark Urzo02:09:15

Oh yeah, that's really helpful. Should probably get used to them.


I have doc bound to ctrl-; d and source bound to ctrl-; S in my editor so they are easily available 🙂


(aside from helping beginners with REPL sessions like the above, I never actually type into my REPL -- I always type into a source file, sometimes in a comment form, and then eval code directly into the REPL from the editor)

clj 1
Clark Urzo02:09:01

Ahh, since I’m moving around so much and can’t bring a decent keyboard with me all the time, I’m kind of stuck with vim. But hearing your suggestion has led me to this so I’m going to try to replicate your setup. Let’s see how it goes.

Clark Urzo16:09:20

All right, lemme try that


and if you're just using a bare repl, don't netcat just type in the repl. no need for another process involved

Nazar Trut16:09:44

(defn find-not-in-kb
  [expr kb]
  (filter #(and (sequential? %) (= (first %) 'if) (= (second %) (nth expr 1))) kb)
Currently, this looks for an "if" in the first of a sequence and then an random element in the second of sequence, how would i change my function so it looks for the third element of the sequence instead of the second ?

Harley Waagmeester17:09:01

this method used to work for me --> (.getURLs (java.lang.ClassLoader/getSystemClassLoader))but now i get the runtime error --> java.lang.IllegalArgumentException: No matching field found: getURLs

Harley Waagmeester17:09:40

i wonder what has changed and how i might repair this ?


the default classloader after java 9 is no longer a URLClassLoader


(it was never promised to be a URLClassLoader in 8, but you could cast it)

Harley Waagmeester17:09:15

ahh, migration is a good search term

Harley Waagmeester17:09:54

marvellous, a simple (seq (java.lang.System/getProperty "java.class.path")) is working for me in the repl


Note that (java.lang.System/getProperty "java.class.path") returns a string, so (seqing it will return a sequence of characters (which may or may not be what you’re after).


(clojure.string/split (java.lang.System/getProperty "java.class.path") #":") might be more like what you’re after?

Harley Waagmeester17:09:34

yes, i am always surprised at how i mess up data presentation, and splitting on the ";" is just right


No worries - I do it all the time too. I find the REPL and (clojure.pprint/pprint) are my best friends for helping to visualise data. 😉


There are also fancier GUI-based tools that can graphically present Clojure data structures (often integrated with the REPL), but I’ve found (pprint) to be more than enough in most cases.


hi! Functions that have side effects are often named with an ! in the end. How do you name functions that use resources that can change. E.g. querying a db or using other non-immutable resources.


I tend to just use a normal function name, even though the function could return different results over time. Otherwise everything up your call chain to -main would need to be annotated with something indicating "side effects" 🙂


The original intent of ! was to indicate functions that would not be safe in a "transaction" (STM) but its use isn't very consistent -- some folks use it on anything that causes side effects, some only for functions that cause external side effects, some even for functions that call down to such functions.


if you wanted to write a new faq entry for, this is a frequent question - I think you've said the critical bits here


Under Reader and Syntax?


"What do ? and ! mean in function names?" perhaps?


(following "What does an _ mean...")


That sounds like a good place for it


In and next.jdbc, functions that are intended to be "just" queries have regular names (`query`, get-by-id, find-by-keys, plan -- since plan itself doesn't actually do anything, only when you reduce it do you get the side-effects), as opposed to update!, insert!, execute!


(`next.jdbc` is a bit more aggressive about this because execute! and execute-one! can be used for reading the DB as well as updating it, unlike c.j.j -- and there are next.jdbc.plan/select! and next.jdbc.plan/select-one! which are helpers that wrap plan with reductions, even if they are queries)

Nazar Trut18:09:22

How would i filter a seqeunce like this "((if a b) a (if b c)) and only return a "a"

Nazar Trut18:09:22

So i want my filter function to find a specific element in my sequence


you might want some instead

Nazar Trut18:09:49

Ok thank you!!

Michael Stokley20:09:12

is point free style dead?

Michael Stokley20:09:28

what's the latest on point free style. does the community like it?

Michael Stokley20:09:24

if all my functions were unary, i'd feel unconflicted about composing them. but since they aren't, it makes point free style more... involved.


point free was never alive in clojure


Despite the presence of comp and partial, the use of anonymous functions is generally preferred.

🙌 1

Can you say more about it? I think I heard that partial is "better" because it doesn't create a closure. Is #() considered more idiomatic?


Rich is on record (several times) as saying he considers #() to be more idiomatic than partial.


Huh. Thanks, I felt so too initially, actually.


partial definitely does create closures -- look at its source.


Well, I figured it must create at least something equivalent (callable and holding the arguments)


partial creates a closure with multiple arities -- since it doesn't know how many arguments your function takes (so it doesn't know how many are still "missing") so if you have a function that takes four or more arguments, you're going to be running via a variadic function using apply.


If you use #(f arg1 %1 %2) then you get a 2-arity closure and your function f will be directly invoked with three arguments -- so you'll get IDE/linter support on the call of f in terms of validating the arity, and you'll also get runtime arity checking when that closure is called. With partial, you lose any IDE/linter support at the call site when writing the code, and you also defer the runtime arity checking (since you go through another call layer between the source code and the actual call site of your function -- and it might go through apply, pushing the arity checking a little further away from what you wrote. All of which makes errors harder to spot and debug.


Yes, got it, thank you!


FWIW, when I got started with Clojure a decade ago, I was very enamored with comp and partial... but I hardly ever use them nowadays.


(I do still like juxt tho')


So partial seems to be a good fit for when you really want varargs, though? Like (partial + 10)


#(apply + 10 %&) would be the alternative, I guess


But in what contexts would you actually want a varargs (anonymous) function? I can't recall ever using %& in a decade of production Clojure and I think I've only used partial on a variadic function "by accident", i.e., when the actual call(s) of that expression are known arities.


Yeah, just grepped my code here: no instances of %& in well over 100k lines of Clojure (including all my OSS work). I have just a handful of (partial apply =) in test code (but that is not variadic, it takes a single collection argument -- so I could (and should) just use #(apply = %)


Across our entire codebase, we have just over 100 calls to partial in just under 60 files.


Yeah, I can probably come up with an artificial example, but I guess you're right, it's not common at all


By contrast, we have well over 500 anonymous functions with #(..) across nearly 200 files.


(I actually had to check the docs before even mentioning %& for its existence; never used it in real life)


Thank you!


That being said, comp is common when composing transformations as part of a transducer invocation.