Fork me on GitHub
#clojure
<
2017-02-10
>
fenton00:02:54

can i have a record implement multiple protocols? example pls 🙂

seahckr02:02:55

I mean, this looks like a function that takes any arbitrary function, and returns another anonymous function, that has some state in the form of an atom

Alex Miller (Clojure team)02:02:43

although most people would recommend not writing fib as a recursive function in the first place

seahckr02:02:43

specifically in line 13, when function 'f' is applied, I wanted to know if the recursive calls are also memoized

Alex Miller (Clojure team)02:02:05

those internal calls will be calling the original function

Alex Miller (Clojure team)02:02:17

memoization is tricky to use with recursive functions

Alex Miller (Clojure team)02:02:35

but again, most people would suggest trying to avoid recursive functions

Alex Miller (Clojure team)02:02:45

you can write fib as a tail-recursive loop/recur instead

Alex Miller (Clojure team)02:02:51

then memoizing it is fine

seahckr02:02:56

okay that makes a lot of sense @alexmiller! got it, I'm not going to use this in production - just for educational purposes 🙂

Alex Miller (Clojure team)02:02:15

if you google for it, there are a large number of fib variants out there in Clojure

seahckr03:02:17

okay - another quick question - if I repeatedly call the same function - how is the original function f not invoked again? How does the atom persist across multiple function calls?

seahckr03:02:53

for example if we do (defn mfoo (memoize foo))

Alex Miller (Clojure team)03:02:06

if you notice there is a let around the creation of the fn

seahckr03:02:07

and I invoke (mfoo bar) multiple times, how does the second function call access that cache?

Alex Miller (Clojure team)03:02:18

the fn closes over the local binding mem

Alex Miller (Clojure team)03:02:42

so the function itself contains a reference to the atom

Alex Miller (Clojure team)03:02:58

it’s a stateful function

seahckr03:02:32

so for somebody coming from Object oriented languages - atom is like a global variable?

Alex Miller (Clojure team)03:02:03

kind of in effect here, but not really

seahckr03:02:29

not really? can you please elaborate?

Alex Miller (Clojure team)03:02:35

what’s actually happening under the hood is that when you create a fn it creates a Java class

Alex Miller (Clojure team)03:02:57

that class has a field that corresponds to mem

Alex Miller (Clojure team)03:02:24

the instance of the class that you get back has the mem field populated with an instance of an atom

Alex Miller (Clojure team)03:02:51

that field is not publicly accessible - it’s a private field of the function class

Alex Miller (Clojure team)03:02:08

to “use” it, you have to invoke the function which controls access to it

seahckr03:02:38

okay - makes sense. It's a kind of a global but only visible from within the function, kind of like class static

seahckr03:02:45

only accessible by instances of that class

seahckr03:02:56

in c# or java..

Alex Miller (Clojure team)03:02:57

yeah, but it’s not static, it’s a private field

seahckr03:02:56

okay - so I tried running this example https://clojure.org/reference/atoms

seahckr03:02:37

It looks like this example is misleading - it claims that without memoization, the recursive function takes more time

seahckr03:02:49

and with memoization - the very first call takes an order of magnitude less

seahckr03:02:56

what am I missing here?

Alex Miller (Clojure team)03:02:36

I agree that that’s a misleading example :)

Alex Miller (Clojure team)03:02:59

all it’s doing is memoizing the single outer call, not the inner recursive calls

Alex Miller (Clojure team)03:02:23

well, actually that’s kind of tricky as it’s actually rebinding fib to the memoized fib

Alex Miller (Clojure team)03:02:47

depending on your compiler mode (direct linking) that won’t actually work

Alex Miller (Clojure team)03:02:08

so it will work in the case presented there (but with some caveats)

seahckr03:02:20

seems to work for me - I haven't tinkered with the compiler mode (default) - but I see no difference in times

seahckr03:02:34

thank you, this makes a lot of sense. This was driving me nuts for the last 2 hours. peace is resetored

seahckr03:02:32

How can I fix this example? Is all this documentation hosted on github or some place like that?

Alex Miller (Clojure team)03:02:07

so like I corrected above, this is actually doing what it says

Alex Miller (Clojure team)03:02:19

it’s just not particularly obvious how

seahckr03:02:37

whoa, it's memoizing the recursive calls?? how?

Alex Miller (Clojure team)03:02:49

(def fib (memoize fib))

seahckr03:02:59

how does using the same name make it any different?

Alex Miller (Clojure team)03:02:09

def will find the existing var if there is one and re-bind it’s root value (the function)

Alex Miller (Clojure team)03:02:24

so is this actually replacing the var fib with a memoized fib

seahckr03:02:39

my goodness - is the internal function also the memoized variant?

Alex Miller (Clojure team)03:02:39

and the internal call resolves the recursive call via the var (which has changed)

Alex Miller (Clojure team)03:02:02

(assuming you’re not using direct linking, which would not do that)

seahckr03:02:26

I want to buy you a beer

Alex Miller (Clojure team)03:02:53

btw, the docs are hosted in github at https://github.com/clojure/clojure-site and issues and prs are welcome there

seahckr03:02:33

okay - I will expand this example to include a little bit of this discussion, so it helps noobs like me

Alex Miller (Clojure team)03:02:25

if you mean on this atom page, I would keep that brief, but happy to take a look. if you’re sending a PR, you also need to sign the contributor agreement https://clojure.org/community/contributing_site

seahckr03:02:35

just one final question - so rebinding the memoized function to itself is then a neat trick to memoize recursive functions?

Alex Miller (Clojure team)03:02:43

the big thing to always remember with memoization is that you have created a place to put unbounded unmanaged state

seahckr03:02:48

is it not possible to put a limit on the size of the atom (kind of like chan buffer size in core.async)

Alex Miller (Clojure team)03:02:50

there are contrib libs (core.memoize which uses core.cache) that give you memoization but also give you control over the cache, so it’s this idea but productionized a bit

Alex Miller (Clojure team)03:02:00

no, you can’t limit the size of the atom

seahckr03:02:02

or make it safe by checking the size of the atom inside the memoized function

Alex Miller (Clojure team)03:02:28

yeah, that’s basically what core.memoize adds, with the option for eviction, expiration, etc

seahckr03:02:50

what I want is a (fixed-sized-atom {} ) - I'm thinking I can write a macro to achieve this easily?

seahckr03:02:54

I will try that - good exercise for me

seahckr03:02:36

thank you - this discussion has helped me a lot

Alex Miller (Clojure team)04:02:43

I don’t think you need a macro for that - a function would fit the bill fine

cappy211205:02:15

I'm taking the Up And Running With Clojure course on http://Lynda.com. The person who created the course (Arthur Ulfeldt) suggested I join slack, to get some help with my init.el file. The course has a file downloads section, of which contains the code to pull down the files to setup emacs as a Clojure IDE. Well, apparently the settings in the init.el file are bad, and Emacs displays an error message about retrieving a package. I'm very much new to Clojure, Emacs, needless to say lisp & init.el settings

nicoschneider13:02:31

cappy2112: emacs might not be a good choice for beginners - especially if you have to learn the language and the editor at the same time. LightTable/Nightcode are good editors that will get you up ‘n running quicker 🙂 for a more conventional IDE, try JetBrains’ IntelliJ with the Cursive plugin (free trials)

cappy211204:02:34

Tell that to @U0EL66M19 Ulfeldt, the instructor for the Clojure course I was trying out, on http://Lynda.com

ejelome06:02:25

@cappy2112 you can can ask here: #emacs 🙂

kah0ona09:02:13

clojure.spec question: is it possible to change what is returned from conform'ing an input? I have a string in format 'e1234' (so 'e' prefixed to '1234'). I'd like to have '1234' returned when calling conform on that string, is that possible (or idiomatic at all?)

kah0ona09:02:58

I thought about making a predicate that returned 1234, but that didn't seem to work 🙂

luxbock10:02:05

@kah0ona you can wrap your predicate with s/conformer

pesterhazy11:02:08

Oh how nice it would be to have this patch merged: http://dev.clojure.org/jira/browse/CLJ-1496

pesterhazy11:02:32

Get rid of all those (throw (ex-info "Boom" {}))

karol.adamiec11:02:56

@pesterhazy i do agree, on the other hand is it not that difficult to wrap exinfo yourself?

sveri13:02:01

@cappy2112 I would suggest to not learn clojure and emacs at the same time. Pick an easier to setup IDE like Lighttable or cursive.

cappy211204:02:42

@U0677JTQX tell that to @U0EL66M19 Ulfeldt, he made the course on http://Lynda.com, and recommended emacs with Clojure-specific packages for this course.

sveri08:02:28

@cappy2112 Well, I am not going to argue with him. The emacs vs other editors topic has been dicussed to death. When I did an introductionary at my work for 5 colleagues I chose Lighttable. While it may be not that popular anymore it is easy to setup. The whole development setup took no more than 15 minutes and everybody was set to go. Anyway, I am pretty sure you can follow the tutorial picking a different editor if you do not want to learn emacs. Also, I am not arguing against emacs or cider in general, I think its just to much for a beginner in both spaces.

arthur02:02:26

Is lighttable still being developed?

sveri08:02:56

Yes, I just read a blog post from them about their plans for the future. Even if it was not. I found it the perfect tool for learning, as it shows evaluation results directly in the editor 🙂

plins13:02:10

@cappy2112 , checkout spacemacs. lots of stuff pre configured

plins13:02:30

is anyone aware of how I can setup compojure to write the log msgs into a file instead of stdout??

val_waeselynck13:02:44

@plins this is usually configured at the level of your logging system, not Compojure

plins13:02:48

well theres some logging automatically happening on stdout when I start a compojure-app, any ideas where i should look for instructions?

plins13:02:00

compojure-api app*

schmee13:02:29

plins what does that logging say?

schmee13:02:46

it is probably the underlying java server that is logging stuff

sveri13:02:33

@plins usually on the JVM you need to configure the logging, depending on your library. Mostly its a configuration file in the resources folder like so: https://github.com/sveri/closp/blob/master/resources/leiningen/new/closp/resources/log4j.properties

sveri13:02:40

You could add a file appender there too

plins13:02:32

$ lein ring server-headless
2017-02-10 11:28:05.317:INFO::main: Logging initialized @1286ms
2017-02-10 11:28:14.157:INFO:oejs.Server:main: jetty-9.2.10.v20150310
2017-02-10 11:28:14.208:INFO:oejs.ServerConnector:main: Started ServerConnector@66b561f1{HTTP/1.1}{0.0.0.0:3000}
2017-02-10 11:28:14.210:INFO:oejs.Server:main: Started @10179ms
Started server on port 3000

sveri13:02:44

@plins here is one with a file appender: http://pastebin.com/hjQY3qCJ

plins13:02:20

thanks! 🙂

sveri13:02:53

Although I have to admit I never tried it 😄

jeff.terrell14:02:57

I need to write a parser for a file. I've heard good things about instaparse, but I've never used it. Would y'all recommend it, or is there a different library/approach that people are moving to instead? Would anybody recommend using clojure spec for this? (Happy for threaded replies to keep chatter down on the main thread.)

schmee14:02:30

jeff.terrell: instaparse is alive and well, and is fully awesome. people have experimented with using spec for parsing, but the performance isn’t great, and the consensus on slack and on the mailing list AFAIK is to use a parser instead.

jeff.terrell14:02:26

@U3L6TFEJF - Thanks, that's helpful. 👍

manutter5115:02:54

Yeah, +1 on instaparse, I used it to build an interpreter for an old-school text adventure, worked great.

weavejester15:02:23

@jeff.terrell I’d recommend instaparse. I think it’s maybe the best parser I’ve used.

aaelony17:02:35

Does anyone know of a library that contains a function to lowercase the keys in an existing map of values? The problem is that when destructuring a map with key of :thekey the actual key might be case-insensitive (e.g. :theKEY or :Thekey or other permutations)

aaelony17:02:20

perhaps Specter...

tolitius17:02:22

@aaelony

boot.user=> (require '[clojure.string :as s])
boot.user=> (defn mlk [m] (into {} (for [[k v] m] [((comp keyword s/lower-case name) k) v])))
#'boot.user/mlk
boot.user=> (mlk {:a 42 :B 34 :some-Key 28})
{:a 42, :b 34, :some-key 28}
?

jromine17:02:58

@aaelony medley (http://weavejester.github.io/medley/medley.core.html) has a map-keys function which you could use.

qqq17:02:00

(meta (assoc (with-meta {} :a) :foo :bar))
is there a way to have meta objects "not survive new -object" ? so I had the :a attacked to {}, but when I create {:foo :bar}, it's a new object -- I don't want it to inherit the old meta -- is there a way to tell clojure 'when creating new objects, don't preserve old meta object" ?

tolitius17:02:34

@aaelony: or reduce if you care about number of iterations over it

boot.user=> (defn mlk [m] (reduce (fn [a [k v]] (assoc a (keyword (s/lower-case (name k))) v)) {} m))
#'boot.user/mlk
boot.user=> (mlk {:a 42 :B 34 :some-Key 28})
{:a 42, :b 34, :some-key 28}
you don't need a lib for this

dpsutton17:02:33

you'll need something a little better I'll bet

dpsutton17:02:47

{:KEY {:NESTED 1}}

aaelony17:02:05

thanks @tolitius, @dpsutton string values is fine for now.

dpsutton17:02:26

i wasn't talking about string but about nested keys

dpsutton17:02:48

wasn't sure if you needed to descend into maps and check for case or if just a top level sweep was enough

aaelony17:02:39

the values are mostly numeric in my specific case, it's just that the key names can be case-insensitive

dpsutton17:02:04

my point is if you have a "flat" map or nested maps

dpsutton17:02:29

if you need to walk your map, descending into nested maps and standardize the keys, you'll need something more along the lines of this:

(defn keywordize-keys
  "Recursively transforms all map keys from strings to keywords."
  {:added "1.1"}
  [m]
  (let [f (fn [[k v]] (if (string? k) [(keyword k) v] [k v]))]
    ;; only apply to maps
    (postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m)))

aaelony17:02:18

awesome. Yeah got it. thanks for swift responses.

jsselman17:02:21

Anyone have any links or guides on best practices for using refs? I’m using one as an in-memory DB and am curious if there’s any gotchas I should be aware of

jsselman17:02:49

Examples of using refs in the wild would be nice too

tolitius17:02:06

@aaelony (if nested needed check out specter):

boot.user=> (require '[clojure.string :as s])
nil
boot.user=> (use 'com.rpl.specter)
nil
boot.user=> (transform (walker keyword?) (comp keyword s/lower-case name) {:a 42 :b {:and-thIs 28 :c {:some-Key 34}}})
{:a 42, :b {:and-this 28, :c {:some-key 34}}}

mpenet17:02:48

@jsselman riemann uses refs

mpenet17:02:36

It's the only open source project I know of that's using them

mpenet18:02:08

"serious project"

jsselman18:02:21

Looking now, thanks

jsselman18:02:55

I’m guessing refs aren’t that popular because most of the time atoms will do, but when they don’t it’s preferred to just use a database?

jsselman18:02:58

On closer look, it’s more than I’m using a ref where a DB makes more sense; for example I need to persist the data across deployments, take backups, etc...

aaelony18:02:42

@tolitius, in the off-chance of duplicate keys, I want to concatenate.. e.g. {:ABC 100 :aBC 200 :abc 300} => {:abc "100,200,300"} will need to add that to the specter version

tolitius18:02:36

if the map is flat you can just use reduce (to get you started):

(defn mlk [m] 
  (reduce (fn [a [k v]] 
            (update a (keyword (s/lower-case (name k))) conj v))
          {} m))

=> (mlk {:ABC 100 :aBC 200 :abc 300})
{:abc (300 200 100)}

favila18:02:35

A Clojure book I co-wrote is on sale today. Is that appropriate material for #announcements or too commercial/self-promoting?

Alex Miller (Clojure team)18:02:01

at least in my opinion (I’m not an admin here)

favila18:02:39

Can I use you as a reference later 😉

favila18:02:58

I think it's probably fine, too, from the channel description

Alex Miller (Clojure team)18:02:17

if anyone here has submitted a talk to a Cognitect Clojure conf in the past, I have a question on #conf-proposals for you

qqq18:02:51

in clojure, is it possible to xreate an object x such that (identical? x (:self (meta x))) returns true ?

favila18:02:09

@qqq

(let [x (atom nil)]
  (alter-meta! x #(assoc % :self x))
  (identical? x (:self (meta x))))

qqq19:02:14

@favila: the user of alter-meta! is clever; thanks!

aaelony19:02:18

hmmm. leiningen is suddenly complaining that

Release versions may not depend upon snapshots.
Freeze snapshots to dated versions or set the LEIN_SNAPSHOTS_IN_RELEASE environment variable to override.
What's the best way to resolve this?

aaelony19:02:28

trying to do a lein uberjar

micahasmith19:02:34

@aaelony i think that happens when you're referencing something you shouldnt in your deps in your project.clj

aaelony19:02:16

hmmmm. ok. wonder what that might be...

tanzoniteblack19:02:01

export LEIN_SNAPSHOTS_IN_RELEASE=override; lein uberjar should allow you to actually get your build to run if you're in a rush, but you should probably check out the output from lein deps :tree to see what snapshot you have in your project

aaelony19:02:49

thank-you @tanzoniteblack, that got my build working. What would I look for in lein deps :tree ?

tanzoniteblack19:02:07

try searching for -SNAPSHOT

aaelony19:02:36

nothing SNAPSHOT there as far as I can tell though

tanzoniteblack19:02:08

if you don't mind sharing the output of that command, I'm happy to take a quick look at it

aaelony19:02:15

ah, I see Possibly confusing dependencies found: ...

tanzoniteblack19:02:47

that's normal when your dependencies have some of the same shared dependencies, but require different versions of them

tanzoniteblack19:02:59

not the direct cause of the snapshot issue

aaelony19:02:27

ok. will override for now until I get a chance to dig in

aaelony19:02:30

many thanks

tanzoniteblack19:02:11

if you're ok with putting the output of the lein deps :tree command in a gist on github, I'm willing to take a minute to look over it if you'd like

Alex Miller (Clojure team)19:02:56

version ranges can also get you into this

schmee21:02:46

how do you disable logging for your test namespaces, with either tools.logging or timbre?

hiredman21:02:11

my preference is to have separate logging configuration in tests that redirects log output to a log file

schmee21:02:22

how do you do that?

hiredman21:02:17

it will depend on how you are logging

hiredman21:02:12

my current project uses tools.logging and log4j2, so I have a log4j2 configuration file under test/, and because it is under test/ it is used when running tests or a repl or whatever in development from a checkout. I have another log4j2 configuration file that is baked in to the docker container with the uberjar for deployment

schmee21:02:43

is it possible to have one for the repl and one for tests?

hiredman21:02:07

it would be tricky, and the exact way to do would depend on what build tool you are using

schmee21:02:24

a related question: right now I’m doing something like (clojure.tools.logging/info msg), but what I want is (clojure.tools.logging/info a-logger msg), so I can inject a null logger in the tests

schmee21:02:49

doesn’t look like either tools.logging or timbre has such an API though

hiredman21:02:58

why do you want to do that?

schmee21:02:27

so can control logging through dependency injection, not through config files

hiredman21:02:56

anyway, tools.logging doesn't do that, so you'll have to use something else

schmee21:02:36

heh, to each his own, I want to gauge my eyes out when dealing with all these Java logger config files 😛

hiredman21:02:00

that is what everyone says

hiredman21:02:22

the thing is, it takes maybe a day tops of messing around with it, then you never touch it

hiredman21:02:43

"10. Get into a rut early: Do the same process the same way. Accumulate idioms. Standardize. The only difference(!) between Shakespeare and you was the size of his idiom list - not the size of his vocabulary."

schmee21:02:58

+1 day everytime you want to tweak anything

hiredman21:02:18

I have a set of logging dependencies and configuration files that I just copy around everywhere. Some times I do have to update them when, for example libraries started using log4j2 instead of log4j

hiredman21:02:50

and it just works, and I shake my head in confusion and wonder what everyone else is doing wrong when they get so annoyed with logginig

Keith21:02:03

Does anyone have experience using clojurefx with an FXML file? I'm having an issue where it loads the FXML, but keeps throwing this exception on load: CompilerException java.lang.NoClassDefFoundError: Could not initialize class javafx.scene.control.ToolBar, compiling:

schmee21:02:11

are there any serious alternatives to tools.logging and timbre in the clojure logging department?

schmee21:02:19

fwiw, I “solved” my problem by creating a test_helpers.clj containing the line (timbre/merge-config! {:appenders nil}) and requiring that in each test ns

schmee21:02:55

which I guess might break horribly in some way I’ll find out later ¯\(ツ)/¯

schmee21:02:25

aaaand that is right now

andrea.crotti21:02:50

I was having a look at Transit

andrea.crotti21:02:47

very odd policy for an open source library and even the test files that are needed to run the tests are not in the repo

andrea.crotti21:02:47

I probably won't consider transit for Python if I can't even run tests for it, which is a shame since it looked quite interesting

sveri22:02:05

Lets say I have a vec with x elements and want to get the 4 elements from it at arbitrary positions, how would I do that? Basically I want something similar to select-keys, but vor vecs and positions.

tanzoniteblack22:02:53

(map #(nth my-vec %) [2, 5, 6])

sveri22:02:48

ok, thats pretty succinct, thank you 🙂

tanzoniteblack22:02:37

make sure you're aware of the fact that nth will throw java.lang.IndexOutOfBoundsException if the number is out of bounds

tanzoniteblack22:02:58

(common source of bugs whenever I use nth)

tanzoniteblack22:02:15

(map #(nth my-vec % default-value) [2 5 6]) will prevent the exception from being thrown

sveri22:02:27

Yes, I know, thanks again 🙂

tanzoniteblack22:02:47

I'm not sure that I knew that nth took a default value before just looking that up 😄

weavejester22:02:41

Isn’t nth a linear time lookup? With a vector you can use get.

weavejester22:02:56

Or just the vector itself as a function.

weavejester22:02:10

(map my-vec [2 5 6])

Alex Miller (Clojure team)22:02:56

if the coll is indexed, nth will do a lookup by index

tanzoniteblack22:02:15

@weavejester you are absolutely correct that that would work as well. And possibly be more performant. I generally try to make sure that all of my code will work with either a list/seq/vector instead of making the code specific to one

weavejester22:02:32

@alexmiller Oh really? I thought it was a seq operation and O(n)

tanzoniteblack22:02:35

that way I can switch between a vector and a sequence produced by having done map/`remove`/`filter` on it without having to change my code

weavejester22:02:37

Has it always been that way?

weavejester22:02:50

Huh. TIL 🙂

Alex Miller (Clojure team)22:02:56

it has many special cases

ghadi22:02:17

nth is a pre-protocol protocol

Alex Miller (Clojure team)22:02:38

well, that’s not the weird part imo

Alex Miller (Clojure team)22:02:48

as other coll functions do similar

Alex Miller (Clojure team)22:02:07

it’s weird in that most coll functions give you a perf expectation and are not implemented when they can’t

Alex Miller (Clojure team)22:02:39

nth does an indexed lookup in a bunch of special cases but falls back to sequential lookup

Alex Miller (Clojure team)22:02:57

so it kind of is both an indexed collection function AND a sequence function

bhauman22:02:40

so is there a standard way to make a *in* reader with a non-determinant length sequence that I can write to?

bhauman22:02:23

I have been killing myself trying todo this.

bhauman23:02:15

I wrote a custom inputstream with a blockingqueue

bhauman23:02:21

thinking I was clever

bhauman23:02:39

that didn't work out very well

weavejester23:02:17

@bhauman: PipedInputStream + ByteArrayOutputStream?

bhauman23:02:56

I tried pipedinput stream with pipedoutput stream

bhauman23:02:10

that worked but it had strange timeout exceptions

bhauman23:02:20

it wanted it to be alive and not wait to long

bhauman23:02:31

it still worked

bhauman23:02:44

@weavejester I'm going to give it a try

weavejester23:02:07

You should only need PipedInputStream, not PipedOutputStream… I think!

bhauman23:02:43

Bytearrayoutputstream automatically grows!!

weavejester23:02:02

yeah 🙂

weavejester23:02:10

Can anyone see the problem with this code:

(defprotocol Logger
  (log
    [logger level event]
    [logger level event data])
  (log-ex [logger level exception]))

(extend-protocol Logger
  nil
  (log [_ _ _])
  (log [_ _ _ _])
  (log-ex [_ _ _]))

weavejester23:02:51

user=> (log nil :info ::foo)

ArityException Wrong number of args (3) passed to: protocols/eval1345/fn--1346  clojure.lang.AFn.throwArity (AFn.java:429)

weavejester23:02:07

Am I going mad, or should it not do that?

weavejester23:02:05

It works with four arguments.

bhauman23:02:09

ByteArrayOutputStream doesn't do it

weavejester23:02:43

@bhauman I think my recollection of pipedinputstream is inaccurate

bhauman23:02:09

@weavejester I appreciate the help

weavejester23:02:22

Hm, weird, protocols with variable argument lists work on records, but not on nil.

bhauman23:02:25

it's kinda interesting how difficult this is to do

weavejester23:02:25

@bhauman: So a pipedinputstream+pipedoutputstream didn’t work?

bhauman23:02:03

it doesn't like to hang for long periods of time

weavejester23:02:36

There’s nothing in the docs to suggest that piped streams have timeouts.

bhauman23:02:48

I know 🙂

bhauman23:02:07

it may just be internal bookeeping

bhauman23:02:38

because the behavior doesn't change

wei23:02:44

can defmulti have a :post clause?

weavejester23:02:29

I found the solution to my protocol problem though. Turns out that the syntax when using extend is slightly different to the syntax when inlining protocol implementations in a vector when it comes to overloaded functions.

weavejester23:02:12

Sometime I need to write a better-multimethods library 🙂

bronsa23:02:31

I remember there being a good reason why multimethods had pre/post conditions on the defmethods rather than defmulti but can't recall it off the top of my head

weavejester23:02:00

It might be another implementation detail; the defmethods just attach functions to the multimethod object.

weavejester23:02:13

And pre/post are implemented on the fn

sova-soars-the-sora23:02:26

I have a fun puzzle at the moment, I have an atom that has some vectors in it... I want to grab the entirety of one vector that has a specific key... how can I "grab the whole thing when just knowing one attribute" ? let me know if that's not clear enough a query...

bronsa23:02:13

@weavejester not sure about that not being a deliberate design choice tho

weavejester23:02:26

@sova how are the vectors stored?

bronsa23:02:30

note that you can have pre conditions on the multimethods by placing them in the dispatch function

weavejester23:02:59

@bronsa Yeah, but again, isn’t that because the dispatch function is a function, and therefore has pre/post?

wei23:02:17

speaking of better-*, I discovered better-cond the other day and found it pretty nice to use

bronsa23:02:19

I'd say that because of the polymorphic nature of multimethods having post condition for each method makes more sense than havingn one to handle all the future defmethods

bronsa23:02:31

IE it's the only way to allow the multimethods to be open in its return type

weavejester23:02:44

Yeah, but no reason you can’t have both.

wei23:02:51

you could a single post condition for multimethods with a multi-spec

weavejester23:02:07

Also multimethods don’t play nice with namespace reloads. Haven’t figured out a good way of solving that one, though.

wei23:02:25

remove-ns 😕

weavejester23:02:09

@wei doesn’t work, unless you remove the namespace that has defmulti. The defmethods aren’t attached to the namespaces they’re defined in.

bronsa23:02:35

@weavejester (def my-multi nil) (defmulti my-multi dispatch-fn) is what people usually do

weavejester23:02:04

Right, but again you need to wipe the defmulti

bronsa23:02:19

to get around the defonce-like semantics

weavejester23:02:41

My current theory is to have better-multimethods create hidden vars in the namespaces they were defined in.

bronsa23:02:32

I think the counter argument is that if you're redefining a defmulti, all the defmethods should be invalidated because you've changed your dispatch funcntion

bronsa23:02:45

and the dispatch value of your old defmethods might not make sense anymore

bronsa23:02:05

(CLOS has a way of dealing with that IIRC but we're nowhere near that level of sophistication)

bronsa23:02:30

i might be completely misunderstanding what you're proposing tho

weavejester23:02:44

Like:

(defmacro defmethod [sym dispatch & fn-tail]
  (let [hidden-sym (gensym (str sym))]
    `(do (defn ~hidden-sym ~@fn-tail)
         (.addMethod ~sym ~dispatch ~hidden-sym))))

bronsa23:02:11

what would that buy you?

weavejester23:02:53

If the namespace is cleared or removed, the multimethods in it are cleared and removed as well.

weavejester23:02:12

Well, clearly my implementation isn’t great - but add some find-vars etc. in there

weavejester23:02:22

Make the gensym predictable based on the key...

weavejester23:02:37

But then it would place nice with tools.namespace.

weavejester23:02:09

Alternatively, attach the namespace to the metadata of the function, and let tools.namespace clear away all multimethods with that metadata.

bronsa23:02:14

you can kind of do that already if you're feeling hacky

weavejester23:02:26

But that would require changes to clojure.core and tools.namespace, so...

bronsa23:02:50

user=> (defmulti foo identity)
#'user/foo
user=> (ns foo)
nil
foo=> (defmethod user/foo 1 [_])
#object[clojure.lang.MultiFn 0x15615099 "clojure.lang.MultiFn@15615099"]
foo=> (methods user/foo)
{1 #object[foo$eval20$fn__21 0x41975e01 "foo$eval20$fn__21@41975e01"]}

bronsa23:02:03

unmunge the fn class name to get the namespace

bronsa23:02:18

and then it's just a matter of remove-method

bronsa23:02:26

not pretty but possible

weavejester23:02:34

Oh, huh. Yeah, I hadn’t considered unmunging the function name.

bronsa23:02:55

really not pretty tho :)

weavejester23:02:09

No, but for a dev-time tool that approach definitely has promise.

weavejester23:02:14

I just want it for refresh

weavejester23:02:09

I might steal that hack, @bronsa 🙂

bronsa23:02:21

feel free to do so :)

Alex Miller (Clojure team)23:02:21

There is a public demunge method in Compiler btw :)

bronsa23:02:43

@alexmiller if there was any chance of having defmethod include the namespace as meta on the method function that hack would be unnecessary.. just planting the idea :P

Alex Miller (Clojure team)23:02:19

Well I'd be fine with that :)

bronsa23:02:20

now that I think about it for more than 12 seconds, metadata on fn objects has potentially non-insignificant performance overhead

arrdem23:02:37

it very much does :c

arrdem23:02:47

see the last time we messed with this