Fork me on GitHub
#clojure
<
2020-01-12
>
seancorfield04:01:13

Yeah, it supports a file system abstraction over other things as well, such as a ZIP file, for example. I use that in depstar

didibus04:01:20

Well, I'm not too sure, but I'd assume there can be many filesystems in use, say you have a USB key plugged in for example, and your hard-drive, that would now be two accessible file systems

didibus04:01:03

Hum, ya, that's where I'm not really sure of any details 😛

deleted04:01:24

and so if the library is being asked to join paths, for instance, you want to know the file system

noisesmith19:01:08

a File object can already do path joining for you in the constructor and static methods as appropriate, doesn't that already resolve the issue?

noisesmith19:01:55

it should use the fs that the File itself would end up on?

noisesmith19:01:21

I'm probably missing something here

noisesmith19:01:58

what I'm saying is the constructor for File should know which fs it ends up on based on the path, and use the correct separator

didibus04:01:59

Paths is one thing I was thinking

didibus04:01:34

Like if a NTFS partition is mounted on linux

didibus04:01:04

Though I'm guessing "mounting" would actually have the OS adapt the NTFS filesystem to the current one

seancorfield04:01:13

@deleted-user dynamic variables are... problematic... start by assuming the filesystem will be passed into all your functions and provide an extra arity that calls each function with the default filesystem).

seancorfield04:01:52

Where shall I start? 🙂

seancorfield04:01:55

I don't have the time/patience tonight for it to be honest, sorry. Just don't do it 🙂

seancorfield04:01:14

I'll be happy to explain when I'm back at work on Monday tho'...

didibus04:01:21

I only know that you have to be careful not to depend on them in a context that runs after the binding is gone.

didibus04:01:39

And thread boundaries

seancorfield04:01:05

It's really problematic if a library uses a dynvar because then you can't have multiple instances in use and, yeah, the threading issues. And it makes testing harder.

seancorfield04:01:18

And it's not considered idiomatic either.

didibus04:01:58

Hum.. isn't the whole point of dynamic var is they enable multiple instances?

seancorfield04:01:25

@didibus No, they break that use case.

didibus04:01:04

I'm not following that I guess. Each caller has it's own binding established no?

seancorfield04:01:38

For example, when I took over clojure.java.jdbc, it relied on a dynvar for the DB and that made it really hard to use in code that needed to talk to multiple databases because you end up being forced to call binding everywhere around every call to the library.

didibus04:01:50

Ok, I see what you mean in that sense.

didibus04:01:01

I normally use dynvars for cross-cutting concerns

didibus04:01:15

Like injecting a specific logger or metric object

didibus04:01:13

They're also better then global configs.

didibus04:01:35

But Sean is right, that for options, having an options map might be better

didibus04:01:47

Well, I'm not against them, and I do use them, like I said, for cross-cutting concerns.

didibus04:01:19

As long as you are careful around lazy-seq and thread boundaries.

didibus04:01:41

But I think what I got from Sean and his java.jdbc example, it is a usability thing for the library

didibus05:01:04

Say I want the cwd to be set to x/y/z for a number of operations

didibus05:01:48

Now I need to either do everything inside one (with-cwd ...) or I need to keep repeating (with-cwd x/y/z) over and over in many places

didibus05:01:20

Where as it might be better to allow the user to do: (def options {:cwd "x/y/z"})

didibus05:01:12

And then do whatever I want passing those along

seancorfield05:01:57

You need to consider whether folks using your lib are likely to need multiple filesystems or not (in your case).

didibus05:01:07

Ya, and if I really didn't want to repeat myself, I could partial all the functions or something like that too, if I don't like typing options on every call

seancorfield05:01:15

But depstar is pretty simple and it already needs two filesystems.

didibus05:01:06

It can depend if you're going for ultimate convenience or flexibility.

didibus05:01:28

Taking an options map I think is a good middleground between the two.

didibus05:01:22

Ya, that can make sense too. Probably easier to add bindings over API that take options already then the other way around

didibus05:01:16

But, if you're imaging it being command line driven, I could see even going for a global context.

didibus05:01:41

Well, I mean like, would you ever want two cwd at the same time? If not, the global context can be locked or using an atom under the hood for example

didibus05:01:13

(change-cwd "x/y/x) could swap! some atom for example

didibus05:01:21

Ah okay then

didibus05:01:17

Dynvar or options map are similar-ish, but with dynvar you need to nest things to use the configured values, where with options you can have things flat and thus more easily reused in varying contexts. I'd say that's the biggest difference in terms of usability.

didibus05:01:51

Do you know if babashka does binding conveyance ?

didibus05:01:55

Like on future?

didibus05:01:47

I tried, it seems it does

didibus05:01:06

user=> (def ^:dynamic *a* 100)
#'user/*a*
user=> (binding [*a* 200] *a*)
200
user=> (binding [*a* 200] @(future *a*))
200

didibus05:01:30

And this is an example of what to be aware with dynvar:

user=> @(binding [*a* 200] (delay *a*))
100

didibus05:01:01

So that's something your users would need to be aware of as well

didibus05:01:44

user=> (def ^:dynamic cwd "z/y/z")
#'user/cwd
user=> (defn read-file "A mock to demonstrate" [file] (str cwd))
#'user/read-file
user=> (binding [cwd "a/b/c"] (map read-file ["file1.txt" "file2.txt"]))
("z/y/z" "z/y/z")

didibus05:01:07

This is probably the worst offender which will trip up people

didibus05:01:02

well I'm making up here that read-file is from your library, but because I call it in a lazy seq and it is realized after the binding context has exited, it is using the wrong cwd

didibus05:01:15

haha, ya, I mean again you'd know best how you anticipate your lib to be used and if these edge cases would be common issues faced by users or not

didibus05:01:48

just laying down the edge cases I've faced in the past with dynvars

borkdude08:01:04

@didibus Yes, babashka supports binding conveyance

murtaza5213:01:34

I have a list of symbols (def my-sym '[ a b]). These symbols have not been declared yet. In a function -

(defn abc
  [a b]
 my-sym)
I want the above fn (abc 1 2) => [1 2] however the above returns the unevaluated [a b] symbol names. How do I get the fn to return [1 2]

Premm Krishna Shenoy07:01:51

If I am right. def is evaluated on loading the file and is not a runtime. http://squirrel.pl/blog/2012/09/13/careful-with-def-in-clojure/

kulminaator13:01:21

with def you define a value

kulminaator13:01:00

why do you expect it to behave like a function afterwards ? 🙂

kulminaator13:01:21

or like a macro .. i'm not even sure what you have meant here

murtaza5213:01:14

There is a list of symbols which I need to reuse in multiple places, I could have written the fn simply to return [a b], but then I would need to keep my multiple fns in sync

kulminaator13:01:54

i think you are expecting def to behave like #define in C

kulminaator13:01:21

like a templating tool ? 🙂 but that's not what it is ...

jumpnbrownweasel13:01:54

It is usually not a good idea to assume local var names when reusing code. But perhaps something like this would help remove redundancy?

(defn abc [& args]
  (vec args))

âž• 8
vlaaad15:01:21

Also known as vector

jumpnbrownweasel18:01:46

Right :-) I had to assume that the example given was a partial snippet, but if that's all that's needed then of course vector will do it.

jumpnbrownweasel13:01:30

Macros can be used to do such things, but like I say it is not a good idea to assume local var names.

murtaza5213:01:08

cant I make it work without macros, something like (eval (map symbol my-sym)) (which gives an error)

Mustakim Patvekar13:01:29

Obviously it will give error because when you try to

(eval [a b])
 
it will say
Unable to resolve symbol: a in this context
because it is not defined in the context

Mustakim Patvekar13:01:15

If you want a function which simply returns a vector of args passed to it, you can use the function abc as suggested by @UBRMX7MT7

jumpnbrownweasel13:01:44

There may be some way to get eval to work for this. But the common practice is that macros and eval should be avoided unless a function really won't meet the need. I don't use eval at all.