Fork me on GitHub
#boot
<
2017-02-02
>
richiardiandrea01:02:59

hey folks, how to use boot.core/cp? how can I get the destination tmp-file?

micha01:02:32

@richiardiandrea it's really for copying the contents of one tmpfile to another

micha01:02:47

it's not really that useful

richiardiandrea01:02:24

uhm, I have a java.io.File and a tmp dir, can I use that to copy a file in there?

richiardiandrea01:02:03

like

...
     prod-file (io/file tmp (str prod-dir prod-name))
     dev-file (io/file tmp (str dev-dir dev-name))

(c/cp fileset prod-file dev-file)

micha01:02:32

(let [prod-file (io/file tmp prod-dir prod-name)
      dev-file (->> fileset input-files (by-ext [".foop"]) first)]
  (cp fileset prod-file dev-file))

richiardiandrea01:02:16

uhm understand, so it must already exist on the fileset

micha01:02:31

it's not the most useful function

richiardiandrea01:02:06

can I create an "empty" TmpFile and use cp with it?

richiardiandrea01:02:54

or maybe I can sift :move a file that I don't care about and overwrite it

micha01:02:18

what are you wanting to do?

richiardiandrea01:02:08

I basically have one file in tmp-dir and i want to copy it to another path in the same tmp-dir

micha01:02:51

ah yes that function will work

richiardiandrea01:02:53

uhm

java.nio.file.NoSuchFileException: /home/arichiardi/.boot/cache/tmp/home/arichiardi/git/packages/intersections/nbe/np8ovp/cljsjs/intersections/development/intersections.inc.js397310969257207322.tmp
    file: "/home/arichiardi/.boot/cache/tmp/home/arichiardi/git/packages/intersections/nbe/np8ovp/cljsjs/intersections/development/intersections.inc.js397310969257207322.tmp"
       clojure.lang.ExceptionInfo: /home/arichiardi/.boot/cache/tmp/home/arichiardi/git/packages/intersections/nbe/np8ovp/cljsjs/intersections/development/intersections.inc.js397310969257207322.tmp
    file: "/tmp/boot.user275995671676653650.clj"

richiardiandrea01:02:37

ah ok I was missing a io/make-parents 😄

micha01:02:50

i like how confusing that error always is too

micha01:02:00

"no such file"

micha01:02:19

lol we asked you to make a file, and you say you can't find it

minikomi07:02:32

Hi there! Anyone using boot-dirac with boot-cljs here?

mitchelkuijpers13:02:57

Are there any people here who have run boot-http with -`-ssl`?

mitchelkuijpers13:02:10

When I try to load something I get a Illegal character 0x16 error

mf14:02:00

Hi Guys, I have been reading the instructions RE setting a clojure version on: https://github.com/boot-clj/boot/wiki/Setting-Clojure-version But I'm still not 100% clear on best practice for defining a Clojure dependency in a boot project. In my ClojureScript project I have a local boot.properties file that sets:

BOOT_CLOJURE_VERSION=1.9.0-alpha14`.
In my project build.boot file I have a dependency specified for clojure:
[org.clojure/clojure "1.7.0"]
Ideally I would like to have the version of Clojure dictated by the version specified in boot.properties. If I run boot.repl in my project the version of Clojure is reported as Clojure 1.9.0-alpha14 - which appears to be what I want. Is this the boot version that all my project boot tasks will run against? If I run boot show -d | grep org.clojure/clojure I see that the dependency is listed as [org.clojure/clojure "1.7.0"], is this pretty much irrelevant providing my app is transpiled via a boot task? Thanks

pesterhazy14:02:51

why not specify the correct version of clojure in build.boot?

pesterhazy14:02:07

also boot 2.7.1. improves this sort of reporting

mf14:02:52

@pesterhazy I'm only setting a diff version in build.boot to test which version takes presedence

mf15:02:40

Ideally I would like to just specify the version in one location

mf15:02:53

@pesterhazy: I see the improved reporting in 2.7.1 which states the following when I run a boot task:

Classpath conflict: org.clojure/clojure version 1.9.0-alpha14 already loaded, NOT loading version 1.7.0
I guess this confirms that the version specified in boot.properties takes presedence.

mf15:02:11

Thanks for pointing this out

pesterhazy15:02:21

you're welcome

mf15:02:16

I'm still unclear if I should be using :scope provided for my Clojure dependency in build.boot:

[org.clojure/clojure "1.7.0" :scope "provided"]

This seemingly acts as documentation that we expect the Clojure version to be controlled by the container (boot?). But apart from that it doesn't seem to have any effect.

richiardiandrea15:02:37

@minikomi I use Dirac from powerlaces/boot-cljs-devtools

alandipert15:02:32

Saw this, might apply to those running macos Sierra: http://stackoverflow.com/questions/39636792/jvm-takes-a-long-time-to-resolve-ip-address-for-localhost might make startup much faster

geoffs16:02:31

@mf this is one way to specify the clojure version only in the boot.properties file https://github.com/boot-clj/boot-war-example/blob/master/build.boot#L4

Niclas16:02:31

Is it possible to get the output from dosh as a string? I’ve tried this with no success:

(with-out-str (dosh “pwd”))

micha16:02:37

@looveh if you don't want the streaming output you can use clojure.java.shell/sh

micha16:02:54

that will block until the program exists

micha16:02:04

and you will get stdout and stderr as strings

micha16:02:18

you can know PWD without running a shell command btw, if that is what you want

mf16:02:20

@geoffs: I have tried following in build.boot:

[org.clojure/clojure ~(clojure-version)]
but I get the following exception when I run a boot task:
java.lang.IllegalArgumentException: Bad artifact coordinates org.clojure:clojure:jar:(clojure.core/unquote (clojure-version)), expected format is <groupId>:<artifactId>[:<e
xtension>[:<classifier>]]:<version>
FYI my project is a ClojureScript project, not sure if this is relevant

Niclas16:02:48

@micha Thanks, that worked like a charm!

geoffs16:02:48

notice the use of template in the build.boot I referenced

micha16:02:51

@looveh you can do (.getCanonicalPath (java.io.File. "."))

Niclas16:02:33

@micha Yeah, my real case is a bit more complex where shell is reasonable, used pwd here for an easy example 🙂

geoffs16:02:39

@mf the template macro is like syntax quote, which is why you need the ~

micha16:02:46

haha ok great 👍

mf16:02:57

@geoffs Not seen that before

mf16:02:02

thanks for the heads up

geoffs16:02:33

@alandipert is there a reason that you use template instead of the syntax-quote?

alandipert16:02:05

i didn't want to resolve symbols to vars

geoffs16:02:09

ohhh, right

geoffs16:02:13

:thumbsup:

geoffs16:02:51

@mf ^^ that's another thing to be aware of with template 🙂

mf16:02:05

@geoffs Thanks for bringing this to my attention, I had previously given up on using (clojure-version)

mf16:02:15

but this is pretty much what I need

mf17:02:03

@geoffs @alandipert Are there circumstances when using template over syntax-quote will be problematic?

micha17:02:09

probably don't want to use it to make macros

micha17:02:18

since it won't resolve names for you

micha17:02:53

unless you specifically want to have macros that are not "hygienic"

micha17:02:38

boot.user=> (defmacro foop [x] (template (inc ~x)))
#'boot.user/foop
boot.user=> (foop 100)
101
boot.user=> (let [inc dec] (foop 100))
99

micha17:02:35

boot.user=> (defmacro foop [x] `(inc ~x))
#'boot.user/foop
boot.user=> (foop 100)
101
boot.user=> (let [inc dec] (foop 100))
101

mf17:02:14

@micha just digesting that 🙂

micha17:02:47

boot.user=> (defmacro foop [x] `(inc ~x))
#'boot.user/foop
boot.user=> (macroexpand '(foop 100))
(clojure.core/inc 100)

micha17:02:02

boot.user=> (defmacro foop [x] (template (inc ~x)))
#'boot.user/foop
boot.user=> (macroexpand '(foop 100))
(inc 100)

micha17:02:05

in the second case the macro does not specify what the inc symbol actually refers to

micha17:02:19

resolution happens after the macro has been expanded

micha17:02:44

template is mostly useful for manipulating data that has symbols in it, but that you won't be evaluating

micha17:02:22

like {:foop (template (a list of symbols we dont want to evaluate with ~some ~we ~do))}

mf17:02:05

@micha thanks so much for the detailed answer. So in the use case of using template in the build.boot to enable the use of (clojure-version), if I follow correctly clojure-version won't be resolved until after the macro has been expanded.

micha17:02:20

well in that case you actually have like (template [[org.clojure/clojure ~(clojure-version)] ... right?

micha17:02:57

ok yeah so ~(clojure-version) is evaluating the expression (clojure-version) during macroexpansion

micha17:02:12

macros are functions that return expressions

micha17:02:40

(clojure-version) will be evaluated while the macro function is doing its thing

micha17:02:01

well in this case it's not a macro, it's template

micha17:02:06

boot.user=> (template [[org.clojure/clojure (clojure-version)]])
[[org.clojure/clojure (clojure-version)]]
boot.user=> (template [[org.clojure/clojure ~(clojure-version)]])
[[org.clojure/clojure "1.8.0"]]

mf17:02:07

I'm a noob when it comes to macros I'm afraid 🙂

micha17:02:24

yeah template is a macro

micha17:02:44

it's a little confusing to explain because everything is interleaved in lisp

micha17:02:51

and recursive

micha17:02:57

also interesting to note:

micha17:02:01

boot.user=> `[[org.clojure/clojure ~(clojure-version)]]
[[org.clojure/clojure "1.8.0"]]
boot.user=> `[[clojure ~(clojure-version)]]
[[boot.user/clojure "1.8.0"]]

micha17:02:20

^^ that's using syntax-quote

micha17:02:09

the best way to get a handle on this macro business is just to try different things and develop some kind of intuition for how it should behave

micha17:02:30

kind of like regular expressions

mf17:02:32

@micha yeah [[org.clojure/clojure ~(clojure-version)]] works in the repl, but boot blows up when I do this in build.boot.

micha17:02:23

@mf it doesn't work for me in the repl:

micha17:02:28

boot.user=> [[org.clojure/clojure ~(clojure-version)]]
       java.lang.ClassNotFoundException: org.clojure
clojure.lang.Compiler$CompilerException: java.lang.ClassNotFoundException: org.clojure, compiling:(/tmp/boot.user7789718177828493815.clj:7:1)

micha17:02:44

it's trying to resolve the var org.clojure/clojure

micha17:02:09

it doesn't find a clojure namespace org.clojure, so it assumes it must be a class

mf17:02:14

Sorry I missed out the syntax-quote in my example

micha17:02:37

oh interesting

mf17:02:05

This works in repl: `[[org.clojure/clojure ~(clojure-version)]]

micha17:02:16

well anyway, syntax quote would do weird stuff like for dependencies without namespaces

micha17:02:26

like [[ring "1.2.3"] ... for example

micha17:02:45

syntax-quote would resolve that to [[boot.user/ring "1.2.3"] ...

micha17:02:48

which is nonsense

mf17:02:56

oh right

micha17:02:59

that's where template comes in

mf17:02:05

ok gotcha

micha17:02:29

and note that we're not evaluating the dependencies vector as code

micha17:02:46

so resolving vars is not really osmething we need to do

micha17:02:59

for deps a symbol is just a symbol and nothing more

micha17:02:31

syntax-quote kind of assumes that symbols refer to names in namespaces or the environment

micha17:02:42

and it tries to help you get it right

mf17:02:27

Ok, don't want to take up too much of your time, but thank you so much for all the info.

mf17:02:15

I can see that I need to get my feet wet with writing some custom macros

micha17:02:52

yeah! macros are super useful

mf17:02:23

Yeah I guess up until now I have put macros on the back-burner, mainly because I feared there use makes it harder for others to grok what's going on in your code (i.e. a bit magical) e.g. when code-reviewing etc.

micha17:02:05

it's a double-edged sword

mf17:02:05

But then again if everyone understands the problem that the macro is solving then I suppose it's not an issue

micha17:02:18

my personal principle is that macros only do syntactic transformations, like just rearranging expressions so they can call functions to do the real work

mf17:02:21

I guess the hard thing for devs coming to a Lisp is knowing when to reach for a macro

micha17:02:45

i mean except for macros you use to do computing at compile time for optimization purposes

micha17:02:45

like anything you do with a macro should be possible to do by calling the functions the macro expands to, just with some more boilerplate typing

micha17:02:13

that's vs the macro expanding to a bunch of code that's branching and so on

micha17:02:27

boot.user=> (source future)
(defmacro future
  "Takes a body of expressions and yields a future object that will
  invoke the body in another thread, and will cache the result and
  return it on all subsequent calls to deref/@. If the computation has
  not yet finished, calls to deref/@ will block, unless the variant of
  deref with timeout is used. See also - realized?."
  {:added "1.1"}
  [& body] `(future-call (^{:once true} fn* [] [email protected])))

micha17:02:45

future-call is doing the real work, it's just a function

mf17:02:46

I will try and follow your principal of letting the macros call out to functions that do the real work

micha17:02:02

the macro simply constructs the anonymous fn to pass to future-call

mf17:02:11

gottcha

micha17:02:12

so you don't need to type the extra parens and fn

mf17:02:22

Yeah that's nice

mf17:02:37

but there not composable right?

micha17:02:44

since the real feature was implemented as a function people don't need to use the macro if they don't want to

micha17:02:00

well this is the most composable way, since it's expanding to a function call

mf17:02:22

Yeah nice to provide people with the macro and function options

micha17:02:55

if you don't provide the implementation as a function you have a problem

mf17:02:55

Ok so if a macro expands to a function call, it can be used with comp etc?

micha17:02:04

because you can't have an anonymous thing then

micha17:02:24

it's meaningless to pass future to a higher order function

micha17:02:38

but you can pass future-call to a higher order function just fine

micha17:02:48

since it's a function itself

micha17:02:52

a first class thing

mf17:02:58

yeah I follow that

micha17:02:03

well comp is a higher order function

micha17:02:10

so you can't pass it a macro

mf17:02:14

but I mean can a macro return a function?

micha17:02:19

because macros are not first class objects

micha17:02:29

they're syntactic transformations

micha17:02:42

macros can't return anything but code

micha17:02:57

the thing they return will be evauated by the compiler

micha17:02:06

i mean by eval

micha17:02:22

so eval needs to know how to eval it

micha17:02:38

and you can't eval a procedure

mf17:02:35

but couldn't the macro resolve to a function when expanded?

micha17:02:03

what do you mean by "function" there?

micha17:02:15

you mean the expression that will evaluate to a function?

micha17:02:21

or an actual function

mf17:02:39

So say I have a macro that when expanded produces an anonymous function

dave18:02:41

mf: like this?

(defmacro foo
  [x]
  `(fn [y#] (+ ~x y#)))

dave18:02:03

the backtick "quotes" the following form

dave18:02:32

and the ~ "unquotes" x in this case, so that the value of x is passed in when the macro expands

dave18:02:25

boot.user=> (defmacro foo
       #_=>   [x]
       #_=>   `(fn [y#] (+ ~x y#)))
#'boot.user/foo
boot.user=> (foo 5)
#object[boot.user$eval7797$fn__7798 0x6d1edab9 "[email protected]"]
boot.user=> ((foo 5) 6)
11
boot.user=>

mf18:02:36

@U0AHJUHJN just saw your thread.... So yeah I can pass the expanded version of foo to a higher-order-function:

((comp (foo 5) inc) 6)

;; => 12

mf19:02:43

So in this case add kinda acts like a partially applied function:

(defmacro add
  [x]
  `(fn [y#] (+ ~x y#)))

((comp (add 1) inc) 1) ;; => 3

((comp (partial + 1) inc) 1) ;; => 3

dave20:02:02

yep, exactly

mf08:02:10

@U0AHJUHJN really appreciate your input, thanks 🙂

micha17:02:57

(defmacro foop [x] (fn [y] (+ y x)))

micha17:02:59

like that?

micha17:02:08

yeah that would not work

micha17:02:19

because the thing the macro returns will be fed into eval

mf17:02:38

which means the function would be called

micha17:02:38

whoa that's interesting

micha17:02:56

boot.user=> ((eval (fn [x] (+ x 1))) 100)
101

micha17:02:02

i'm surprised that works that way

micha17:02:14

i think it's undefined behavior kind of

micha17:02:17

boot.user=> (doc eval)
-------------------------
clojure.core/eval
([form])
  Evaluates the form data structure (not text!) and returns the result.

micha17:02:35

a function is definitely not a form data structure

micha17:02:04

but for whatever reason the compiler will pass it through

mf18:02:56

@micha I said I wasn't going to take up all your time 🙂 You've got me eager to play with macros now!

mf18:02:17

Thank you for all your fantastic info!

micha18:02:55

have fun!

witek21:02:19

Hi. I am using boot-cljs with boot-reload. Works fine, when changing source files in the app. But I also have a library, which runs with watch pom jar install. And when I make changes to source files there, the changes do not get hot-loaded into my app. I have to restart my app boot and then I have to reload my page. Is there a trick to also hot-reload dependencies?

geoffs21:02:45

@witek have you specified the library as a checkout dependency? https://github.com/boot-clj/boot/issues/154

witek21:02:33

@geoffs Thank you for this info. Seams the task is deprecated. Now there is the -c option to boot itself. Will try this...

geoffs22:02:57

good to know, I didn't realize it had been deprecated

witek22:02:12

works perfect! 😄

witek22:02:25

Is there a way to activate the -c option inside my build.boot file instead of having to pass it as a parameter on the command line?

geoffs22:02:31

I think you can maybe use the :checkouts key in set-env!

witek22:02:57

yes, thats it. thank you!

mobileink23:02:22

anybody have a boot/new tutorial? i would like to create a template for my project, but the docs at https://github.com/boot-clj/boot-new are totally useless.